POMDPPlanners.tests.test_core.test_belief package
Submodules
POMDPPlanners.tests.test_core.test_belief.belief_equivalence_utils module
Shared belief-level equivalence checks for particle beliefs.
This module provides environment-agnostic assertion helpers that compare a
standard WeightedParticleBelief
against a
VectorizedWeightedParticleBelief
through their public interface (update, normalized_weights, sample).
Callers build the two beliefs with aligned particles and weights; the helpers
run the supplied action/observation through both paths and assert the expected
invariants. Each update-invoking helper seeds numpy’s global RNG
identically on both paths so stochastic transitions and observation draws
consume the same random numbers, mirroring the pattern used in
POMDPPlanners.tests.test_core.test_belief.vectorized_updater_test_utils.
- Functions:
assert_update_particles_match: Next particles agree after one
update. assert_update_weights_match: Normalized weights agree after oneupdate. assert_update_top_k_ranking_agrees: Top-K particle ranking agrees. assert_update_equivalence: Combined particle + weight + ranking check. assert_chained_update_equivalence: Invariants hold across a sequence of updates. assert_normalized_weights_match: Pre-update weight-vector sanity check. assert_sample_distributions_match: Empiricalsamplehistograms agree.
- POMDPPlanners.tests.test_core.test_belief.belief_equivalence_utils.assert_chained_update_equivalence(base, vec, steps, pomdp, atol_particles=1e-10, atol_weights=1e-05, seed=None, particle_to_array=None)[source]
Run a sequence of updates and assert final particles and weights agree.
- Parameters:
base (
WeightedParticleBelief) – BaselineWeightedParticleBeliefwith aligned particles.vec (
VectorizedWeightedParticleBelief) – Vectorized belief holding the same particles asbase.steps (
Sequence[Tuple[Any,Any]]) – Iterable of(action, observation)pairs applied in order.pomdp (
Environment) – Environment used by the baseline’s per-particle transition loop.atol_particles (
float) – Absolute tolerance for the final particle comparison.atol_weights (
float) – Absolute tolerance for the final normalized-weight comparison.seed (
Optional[int]) – If provided,numpy’s global RNG is seeded to this value before each path of every step.
- Return type:
Tuple[WeightedParticleBelief,VectorizedWeightedParticleBelief]- Returns:
The final
(base, vec)belief pair.
- POMDPPlanners.tests.test_core.test_belief.belief_equivalence_utils.assert_normalized_weights_match(base, vec, atol=1e-10)[source]
Assert the two beliefs expose the same probability vector over particles.
Intended as a pre-update sanity check on aligned beliefs.
- Parameters:
base (
WeightedParticleBelief) – BaselineWeightedParticleBelief.vec (
VectorizedWeightedParticleBelief) – Vectorized belief.atol (
float) – Absolute tolerance for the weight comparison.
- Return type:
- POMDPPlanners.tests.test_core.test_belief.belief_equivalence_utils.assert_sample_distributions_match(base, vec, n_samples=20000, tol=0.03, atol_weights=0.03, seed=None, particle_to_array=None)[source]
Assert
sampleoutput distributions agree across beliefs and match weights.Draws
n_samplesfrom each belief, aggregates by particle identity (duplicate particle rows are merged into their first occurrence so discrete-state environments are handled correctly), and runs three checks:The two empirical histograms agree in L-infinity within
tol.base’s histogram agrees with its ownnormalized_weightswithinatol_weights(verifiesbase.sample()is unbiased).Same for
vec(verifiesvec.sample()is unbiased).
Check 1 alone would pass even if both
sample()impls were biased the same way. Checks 2 and 3 close that hole.The helper assumes particles are convertible to fixed-length numeric arrays – the same constraint
VectorizedWeightedParticleBeliefimposes on the particle store itself.- Parameters:
base (
WeightedParticleBelief) – BaselineWeightedParticleBelief.vec (
VectorizedWeightedParticleBelief) – Vectorized belief sharing the same particles asbase.n_samples (
int) – Number of samples drawn from each belief.tol (
float) – L-infinity tolerance for the two histograms agreeing.atol_weights (
float) – L-infinity tolerance for each histogram agreeing with its belief’snormalized_weights.seed (
Optional[int]) – If provided,numpy’s global RNG is seeded to this value before each sampling path so the test is deterministic.
- Return type:
- POMDPPlanners.tests.test_core.test_belief.belief_equivalence_utils.assert_update_equivalence(base, vec, action, observation, pomdp, atol_particles=1e-10, atol_weights=1e-06, significance_threshold=0.0001, top_k=5, seed=None, particle_to_array=None)[source]
Run one update and assert particles, weights, and top-
kall agree.Combines
assert_update_particles_match(),assert_update_weights_match(), andassert_update_top_k_ranking_agrees()into a single end-to-end check for callers that want the full equivalence invariant in one line.- Parameters:
base (
WeightedParticleBelief) – BaselineWeightedParticleBeliefwith aligned particles.vec (
VectorizedWeightedParticleBelief) – Vectorized belief holding the same particles asbase.action (
Any) – Action passed to bothupdatecalls.observation (
Any) – Observation passed to bothupdatecalls.pomdp (
Environment) – Environment used by the baseline’s per-particle transition loop.atol_particles (
float) – Absolute tolerance for the next-particle comparison.atol_weights (
float) – Absolute tolerance for the normalized-weight comparison.significance_threshold (
Optional[float]) – Weight-masking threshold passed to the weight check; set toNoneto disable masking.top_k (
int) – Number of top particles by weight compared for ranking agreement.seed (
Optional[int]) – If provided,numpy’s global RNG is seeded to this value before each path.
- Return type:
Tuple[WeightedParticleBelief,VectorizedWeightedParticleBelief]- Returns:
The updated
(base, vec)belief pair.
- POMDPPlanners.tests.test_core.test_belief.belief_equivalence_utils.assert_update_equivalence_per_particle_seeded(base, vec, action, observation, pomdp, atol_particles=1e-10, atol_weights=1e-06, significance_threshold=None, top_k=5, base_seed=0, particle_to_array=None)[source]
Per-particle-seeded variant of
assert_update_equivalence().For each particle index
i, build a single-particle belief on both sides, seednumpy’s global RNG tobase_seed + i, run oneupdate, and stitch the per-particle results back into aggregate beliefs before running the usual particle / weight / top-K checks.Use this helper for environments whose vectorized
batch_transitionconsumes the RNG in a different order than the baseline per-particle loop (e.g. LaserTag, which bulk-samples robot noise then opponent noise while the standard path interleaves them per particle). Under bulk-vs-interleaved ordering, a single shared seed cannot make the two paths agree forN > 1; per-particle seeding restores bit-for-bit equivalence at the cost ofO(N)update calls.- Parameters:
base (
WeightedParticleBelief) – BaselineWeightedParticleBeliefwith aligned particles.vec (
VectorizedWeightedParticleBelief) – Vectorized belief holding the same particles asbase.action (
Any) – Action passed to bothupdatecalls.observation (
Any) – Observation passed to bothupdatecalls.pomdp (
Environment) – Environment used by the baseline’s per-particle transition loop.atol_particles (
float) – Absolute tolerance for the next-particle comparison.atol_weights (
float) – Absolute tolerance for the normalized-weight comparison.significance_threshold (
Optional[float]) – Weight-masking threshold;Nonedisables.top_k (
int) – Number of top particles by weight compared for ranking agreement.base_seed (
int) – Seed for particleiisbase_seed + i.particle_to_array (
Optional[Callable[[Any],ndarray]]) – Optional converter passed through to the particle comparison. Seeassert_update_particles_match().
- Return type:
Tuple[WeightedParticleBelief,VectorizedWeightedParticleBelief]- Returns:
The updated
(base, vec)belief pair built by aggregating the per-particle results.
- POMDPPlanners.tests.test_core.test_belief.belief_equivalence_utils.assert_update_particles_match(base, vec, action, observation, pomdp, atol=1e-10, seed=None, particle_to_array=None)[source]
Run one update on both beliefs and assert next particles agree.
- Parameters:
base (
WeightedParticleBelief) – BaselineWeightedParticleBeliefwith aligned particles.vec (
VectorizedWeightedParticleBelief) – Vectorized belief holding the same particles asbase.action (
Any) – Action passed to bothupdatecalls.observation (
Any) – Observation passed to bothupdatecalls.pomdp (
Environment) – Environment used by the baseline’s per-particle transition loop.atol (
float) – Absolute tolerance for the particle array comparison.seed (
Optional[int]) – If provided,numpy’s global RNG is seeded to this value before each path so stochastic transitions consume identical random sequences.particle_to_array (
Optional[Callable[[Any],ndarray]]) – Optional callable converting a baseline particle to a 1-Dnp.ndarray. Needed whenbase.particlesholds non-ndarray objects (e.g. dataclasses) whose layout matchesvec.particlesrows. Defaults tonp.asarray().
- Return type:
Tuple[WeightedParticleBelief,VectorizedWeightedParticleBelief]- Returns:
The updated
(base, vec)belief pair so callers can chain asserts.
- POMDPPlanners.tests.test_core.test_belief.belief_equivalence_utils.assert_update_particles_match_per_particle_seeded(base, vec, action, observation, pomdp, atol=1e-10, base_seed=0, particle_to_array=None)[source]
Per-particle-seeded variant of
assert_update_particles_match().See
assert_update_equivalence_per_particle_seeded()for why bulk-vs-interleaved RNG ordering forces per-particle seeding. This helper only checks the next-particle array – use it when weight agreement is blocked by thelog(eps + prob)floor inWeightedParticleBelief._update_weightsfor observations that underflow the Gaussian PDF across every particle.- Parameters:
base (
WeightedParticleBelief) – BaselineWeightedParticleBeliefwith aligned particles.vec (
VectorizedWeightedParticleBelief) – Vectorized belief holding the same particles asbase.action (
Any) – Action passed to bothupdatecalls.observation (
Any) – Observation passed to bothupdatecalls.pomdp (
Environment) – Environment used by the baseline’s per-particle transition loop.atol (
float) – Absolute tolerance for the particle array comparison.base_seed (
int) – Seed for particleiisbase_seed + i.particle_to_array (
Optional[Callable[[Any],ndarray]]) – Optional converter passed through to the particle comparison. Seeassert_update_particles_match().
- Return type:
Tuple[WeightedParticleBelief,VectorizedWeightedParticleBelief]- Returns:
The updated
(base, vec)belief pair.
- POMDPPlanners.tests.test_core.test_belief.belief_equivalence_utils.assert_update_top_k_ranking_agrees(base, vec, action, observation, pomdp, k=5, seed=None)[source]
Run one update and assert the top-
kparticle indices agree.- Parameters:
base (
WeightedParticleBelief) – BaselineWeightedParticleBeliefwith aligned particles.vec (
VectorizedWeightedParticleBelief) – Vectorized belief holding the same particles asbase.action (
Any) – Action passed to bothupdatecalls.observation (
Any) – Observation passed to bothupdatecalls.pomdp (
Environment) – Environment used by the baseline’s per-particle transition loop.k (
int) – Number of top particles by weight to compare.seed (
Optional[int]) – If provided,numpy’s global RNG is seeded to this value before each path.
- Return type:
Tuple[WeightedParticleBelief,VectorizedWeightedParticleBelief]- Returns:
The updated
(base, vec)belief pair.
- POMDPPlanners.tests.test_core.test_belief.belief_equivalence_utils.assert_update_weights_match(base, vec, action, observation, pomdp, atol=1e-06, significance_threshold=None, seed=None)[source]
Run one update on both beliefs and assert normalized weights agree.
- Parameters:
base (
WeightedParticleBelief) – BaselineWeightedParticleBeliefwith aligned particles.vec (
VectorizedWeightedParticleBelief) – Vectorized belief holding the same particles asbase.action (
Any) – Action passed to bothupdatecalls.observation (
Any) – Observation passed to bothupdatecalls.pomdp (
Environment) – Environment used by the baseline’s per-particle transition loop.atol (
float) – Absolute tolerance for the weight comparison.significance_threshold (
Optional[float]) – If given, only compare normalized weights for baseline particles withnormalized_weight > significance_threshold. This absorbs thelog(eps + p)floor inWeightedParticleBelief._update_weightsvs. the vectorized path’s direct log-space accumulation, which drives tiny low-weight particles apart without affecting the belief’s effective distribution.seed (
Optional[int]) – If provided,numpy’s global RNG is seeded to this value before each path so stochastic transitions consume identical random sequences.
- Return type:
Tuple[WeightedParticleBelief,VectorizedWeightedParticleBelief]- Returns:
The updated
(base, vec)belief pair.
POMDPPlanners.tests.test_core.test_belief.test_base module
POMDPPlanners.tests.test_core.test_belief.test_belief_environment_integration module
POMDPPlanners.tests.test_core.test_belief.test_belief_utils module
POMDPPlanners.tests.test_core.test_belief.test_gaussian_belief module
POMDPPlanners.tests.test_core.test_belief.test_gaussian_belief_updaters module
Tests for Gaussian belief updater classes.
This module tests the concrete updater implementations: - LinearKalmanFilterUpdater: linear-Gaussian systems - ExtendedKalmanFilterUpdater: nonlinear systems with Jacobians - UnscentedKalmanFilterUpdater: nonlinear systems without Jacobians
- class POMDPPlanners.tests.test_core.test_belief.test_gaussian_belief_updaters.TestExtendedKalmanFilterUpdater[source]
Bases:
object- test_config_id_deterministic()[source]
Test that config_id is deterministic for identical EKF updaters.
Purpose: Validates that the same Q and R produce the same config_id.
Given: Two ExtendedKalmanFilterUpdater instances with identical Q and R. When: config_id is computed for both. Then: Both config_ids are equal.
Test type: unit
- test_config_id_sensitive_to_parameters()[source]
Test that config_id differs when Q differs.
Purpose: Validates config_id sensitivity to noise parameters.
Given: Two ExtendedKalmanFilterUpdater instances with different Q matrices. When: config_id is computed for both. Then: The config_ids are different.
Test type: unit
- test_ekf_covariance_is_symmetric()[source]
Test that the EKF posterior covariance is symmetric.
Purpose: Validates the symmetrization step in the EKF implementation.
Given: A nonlinear EKF system. When: An update is performed. Then: The resulting covariance is symmetric.
Test type: unit
- test_integration_with_gaussian_belief()[source]
Test that the EKF updater works with GaussianBelief.update().
Purpose: Validates end-to-end integration of the EKF class with GaussianBelief.
Given: A GaussianBelief using an EKF updater for a linear system. When: belief.update() is called. Then: Returns a new GaussianBelief with updated parameters.
Test type: integration
- test_linear_system_matches_kf()[source]
Test that the EKF matches the linear KF on a linear system.
Purpose: Validates that the EKF reduces to the standard KF for linear models.
Given: A linear system represented as both KF and EKF. When: Both are updated with the same observation. Then: Posterior means and covariances match within numerical tolerance.
Test type: unit
- test_nonlinear_system_reduces_covariance()[source]
Test EKF on a nonlinear system reduces covariance after observation.
Purpose: Validates that the EKF incorporates observations to reduce uncertainty.
Given: A nonlinear system f(x,u)=x, h(x)=x^3 (cube observation). When: An observation is incorporated. Then: Posterior covariance trace is smaller than predicted covariance trace.
Test type: unit
- class POMDPPlanners.tests.test_core.test_belief.test_gaussian_belief_updaters.TestLinearKalmanFilterUpdater[source]
Bases:
object- test_1d_analytical_values()[source]
Test 1D Kalman filter against hand-computed analytical solution.
Purpose: Validates KF predict-correct matches textbook formula.
Given: A=1, B=0, H=1, Q=0, R=1, prior mean=0, prior cov=1. When: Observation z=2.0 is incorporated. Then: Posterior mean = 1.0, posterior variance = 0.5.
Test type: unit
- test_1d_static_system()[source]
Test 1D Kalman filter on a static system with perfect observation.
Purpose: Validates that the KF converges toward the observation for a static system.
Given: A=1, B=0, H=1, Q=0.1, R=0.5 (1D static system with noisy observation). When: A single predict-correct cycle is performed with observation z=3.0. Then: The posterior mean moves toward 3.0 and covariance decreases.
Test type: unit
- test_2d_tracking_covariance_decreases()[source]
Test 2D Kalman filter covariance reduction over multiple steps.
Purpose: Validates that repeated observations reduce uncertainty.
Given: 2D identity system with Q=0.01*I and R=0.5*I. When: 10 observations at [1, 1] are incorporated sequentially. Then: Covariance trace decreases monotonically.
Test type: unit
- test_config_id_deterministic()[source]
Test that config_id is deterministic for identical updaters.
Purpose: Validates that the same parameters produce the same config_id.
Given: Two LinearKalmanFilterUpdater instances with identical parameters. When: config_id is computed for both. Then: Both config_ids are equal.
Test type: unit
- test_config_id_sensitive_to_parameters()[source]
Test that config_id differs when parameters differ.
Purpose: Validates config_id sensitivity to matrix values.
Given: Two LinearKalmanFilterUpdater instances with different Q matrices. When: config_id is computed for both. Then: The config_ids are different.
Test type: unit
- test_integration_with_gaussian_belief()[source]
Test that the linear KF updater works with GaussianBelief.update().
Purpose: Validates end-to-end integration of the updater class with GaussianBelief.
Given: A GaussianBelief using a linear KF updater. When: belief.update() is called. Then: Returns a new GaussianBelief with updated mean and covariance.
Test type: integration
- test_with_control_input()[source]
Test Kalman filter with a non-zero control input.
Purpose: Validates that the control matrix B shifts the predicted mean.
Given: A=1, B=1, H=1, Q=0, R=1, prior mean=0, prior cov=1. When: Action u=5 and observation z=5 are applied. Then: Posterior mean is close to 5.
Test type: unit
- class POMDPPlanners.tests.test_core.test_belief.test_gaussian_belief_updaters.TestUnscentedKalmanFilterUpdater[source]
Bases:
object- test_config_id_deterministic()[source]
Test that config_id is deterministic for identical UKF updaters.
Purpose: Validates that the same parameters produce the same config_id.
Given: Two UnscentedKalmanFilterUpdater instances with identical parameters. When: config_id is computed for both. Then: Both config_ids are equal.
Test type: unit
- test_config_id_sensitive_to_parameters()[source]
Test that config_id differs when scaling parameters differ.
Purpose: Validates config_id sensitivity to alpha/beta/kappa.
Given: Two UnscentedKalmanFilterUpdater instances with different alpha. When: config_id is computed for both. Then: The config_ids are different.
Test type: unit
- test_higher_dimensional_system()[source]
Test UKF on a higher-dimensional system (d=5 state, p=3 observation).
- Purpose: Validates that the UKF handles dimension mismatches between
state and observation spaces correctly.
Given: A 5D state system with 3D observations via a projection matrix. When: An update is performed. Then: State mean is 5D, covariance is 5x5, and covariance is symmetric PSD.
Test type: unit
- test_integration_with_gaussian_belief()[source]
Test that the UKF updater works with GaussianBelief.update().
Purpose: Validates end-to-end integration of the UKF class with GaussianBelief.
Given: A GaussianBelief using a UKF updater for a linear system. When: belief.update() is called. Then: Returns a new GaussianBelief with updated parameters.
Test type: integration
- test_linear_system_matches_kf()[source]
Test that the UKF matches the linear KF on a linear system.
Purpose: Validates that the UKF reduces to the standard KF for linear models.
Given: A linear system represented as both KF and UKF. When: Both are updated with the same observation. Then: Posterior means and covariances match within numerical tolerance.
Test type: unit
- test_nonlinear_system_reduces_covariance()[source]
Test UKF on a nonlinear system reduces covariance after observation.
Purpose: Validates that the UKF incorporates observations to reduce uncertainty.
Given: A nonlinear system f(x,u)=x, h(x)=x^3 (cube observation). When: An observation is incorporated. Then: Posterior covariance trace is smaller than predicted covariance trace.
Test type: unit
- test_sigma_point_scaling_parameters()[source]
Test UKF with custom alpha, beta, kappa scaling parameters.
Purpose: Validates that non-default sigma point parameters produce valid results.
Given: A UKF with alpha=0.5, beta=2.0, kappa=1.0. When: An update is performed. Then: Output mean has correct shape and covariance is symmetric PSD.
Test type: unit
- test_ukf_covariance_is_symmetric()[source]
Test that the UKF posterior covariance is symmetric.
Purpose: Validates the symmetrization step in the UKF implementation.
Given: A nonlinear UKF system with off-diagonal covariance. When: An update is performed. Then: The resulting covariance is symmetric.
Test type: unit
- test_ukf_matches_ekf_on_linear_system()[source]
Test that UKF and EKF produce identical results on a linear system.
Purpose: Validates UKF-EKF equivalence when the system is linear.
Given: The same linear system represented as both EKF and UKF. When: Both are updated with identical inputs. Then: Posterior means and covariances match within tolerance.
Test type: unit
POMDPPlanners.tests.test_core.test_belief.test_gaussian_mixture_belief module
POMDPPlanners.tests.test_core.test_belief.test_particle_beliefs module
POMDPPlanners.tests.test_core.test_belief.test_vectorized_weighted_particle_belief module
POMDPPlanners.tests.test_core.test_belief.vectorized_updater_test_utils module
Shared test utilities for comparing vectorized vs per-particle belief updates.
This module provides reusable assertion functions that verify vectorized batch operations (batch_transition, batch_observation_log_likelihood) produce the same results as the equivalent per-particle loop through the environment’s state_transition_model and observation_model.
- Functions:
assert_batch_transition_matches_loop: Compare batch_transition vs per-particle transitions. assert_batch_obs_log_likelihood_matches_loop: Compare batch log-likelihoods vs per-particle.
- POMDPPlanners.tests.test_core.test_belief.vectorized_updater_test_utils.assert_batch_obs_log_likelihood_matches_loop(updater, particles, action, observation, per_particle_ll_fn, atol=1e-10, compare_mode='absolute', err_msg='')[source]
Assert that batch_observation_log_likelihood matches a per-particle loop.
- Parameters:
updater (
VectorizedParticleBeliefUpdater) – Vectorized updater to test.particles (
ndarray) – Particle array of shape (N, d).action (
Any) – Action used for the observation model.observation (
Any) – Observation value.per_particle_ll_fn (
Callable[[ndarray,Any,Any],float]) – Callable(particle_1d, action, observation) -> float that wraps the environment’s per-particle log-likelihood logic.atol (
float) – Absolute tolerance for comparison.compare_mode (
str) –"absolute"for direct comparison, or"pairwise_diff"to comparell - ll[0](for environments whose per-particle path omits a normalisation constant).err_msg (
str) – Optional message appended on failure.
- Raises:
ValueError – If compare_mode is not
"absolute"or"pairwise_diff".- Return type:
- POMDPPlanners.tests.test_core.test_belief.vectorized_updater_test_utils.assert_batch_transition_matches_loop(updater, particles, action, per_particle_transition_fn, atol=1e-10, seed=None, err_msg='')[source]
Assert that batch_transition matches a per-particle transition loop.
- Parameters:
updater (
VectorizedParticleBeliefUpdater) – Vectorized updater to test.particles (
ndarray) – Particle array of shape (N, d).action (
Any) – Action to apply.per_particle_transition_fn (
Callable[[ndarray,Any],ndarray]) – Callable(particle_1d, action) -> next_state_1d that wraps the environment’s per-particle transition logic.atol (
float) – Absolute tolerance for comparison.seed (
Optional[int]) – If provided, seeds np.random before each path so stochastic transitions consume the same random sequence.err_msg (
str) – Optional message appended on failure.
- Return type: