POMDPPlanners.tests.test_environments.test_pacman_native package

Submodules

POMDPPlanners.tests.test_environments.test_pacman_native.test_pacman_kernel_cache module

Per-action C++ kernel cache parity tests for PacManPOMDP.

These tests pin down the new per-action _trans_kernel_cache / _obs_kernel_cache behaviour. They guarantee that:

  1. _get_trans_kernel(action) / _get_obs_kernel(action) return the same Python object on repeated calls (cache hit).

  2. observation_log_probability produces numerically identical results when routed through the cached kernel vs. a freshly constructed kernel (atol = 1e-12 — both paths execute the same C++ code).

  3. The env survives a pickle round-trip with the cache dropped, and the restored env behaves identically to the original on sample_next_state, sample_observation and reward_batch.

class POMDPPlanners.tests.test_environments.test_pacman_native.test_pacman_kernel_cache.TestObservationLogProbabilityCachedVsFresh[source]

Bases: object

Numerical parity: cached path vs. freshly built kernel.

test_observation_log_probability_matches_fresh_kernel()[source]

Test that the cached observation_log_probability equals a freshly built kernel’s output.

Return type:

None

Purpose: Validates that routing through the cached kernel gives

byte-equivalent log-probabilities (both paths execute the same C++ code).

Given: A built env, a non-terminal next_state, and a 2-D batch of

candidate observations stacked from 16 sampled obs arrays.

When: env.observation_log_probability(next_state, action, obs_batch)

is called using the cached kernel, and a freshly constructed PacManObservationCpp with the same (next_state, action) is used to compute np.log(probability) directly.

Then: The two arrays are equal within atol=1e-12 for each action.

Test type: integration

class POMDPPlanners.tests.test_environments.test_pacman_native.test_pacman_kernel_cache.TestPerActionKernelCache[source]

Bases: object

Cache hit + sample-driven warming behaviour.

test_repeated_get_obs_kernel_returns_same_object()[source]

Test that _get_obs_kernel is idempotent per action.

Return type:

None

Purpose: Validates that the per-action observation kernel cache

returns the exact same Python object on a second call for the same action.

Given: A freshly constructed PacManPOMDP with empty caches. When: _get_obs_kernel(a) is called twice for each action in

{0, 1, 2, 3}.

Then: id() of the two returned kernels is equal for each action.

Test type: unit

test_repeated_get_trans_kernel_returns_same_object()[source]

Test that _get_trans_kernel is idempotent per action.

Return type:

None

Purpose: Validates that the per-action transition kernel cache

returns the exact same Python object on a second call for the same action, and distinct objects across actions.

Given: A freshly constructed PacManPOMDP with empty caches. When: _get_trans_kernel(a) is called twice for each action in

{0, 1, 2, 3}.

Then: id() of the two returned kernels is equal for each action,

and the four cached objects are pairwise distinct.

Test type: unit

test_sample_next_state_warms_cache_once_per_action()[source]

Test that 100 sample_next_state calls reuse the cached kernel.

Return type:

None

Purpose: Validates the hot-path cache reuse — repeatedly sampling

should not rebuild the kernel.

Given: A freshly constructed env and a sampled initial state. When: sample_next_state(state, action) is called 100 times for

each of the four actions.

Then: After the first call for an action, the cached kernel is

identical to the one returned by _get_trans_kernel and persists across all 100 calls.

Test type: unit

class POMDPPlanners.tests.test_environments.test_pacman_native.test_pacman_kernel_cache.TestPickleRoundTrip[source]

Bases: object

Pickling drops kernels and rebuilds lazily.

test_pickle_drops_caches_and_keeps_identical_behaviour()[source]

Test pickle round-trip preserves env behaviour while dropping kernel caches.

Return type:

None

Purpose: Validates that __getstate__ / __setstate__ strip the

(pybind11, non-picklable) kernels and the restored env behaves identically to the original on the env-level API.

Given: A built env warmed by sampling once for action 0 (so its

transition cache holds one entry).

When: pickle.dumps then pickle.loads runs on the env, and the

restored env is queried with the same (state, action) under shared native and numpy seeds.

Then: The restored env has empty caches, sample_next_state returns

the same shape/values, and reward_batch agrees byte-for-byte on a 4-particle batch.

Test type: integration

POMDPPlanners.tests.test_environments.test_pacman_native.test_pacman_native_equivalence module

Native↔Python equivalence tests for the PacMan POMDP C++ port.

These tests target the PacManTransitionCpp C++ kernel directly via the PacManPOMDP env API (sample_next_state / transition_log_probability / sample_observation / observation_log_probability). The per-call Python wrapper classes (PacManStateTransitionModel / PacManObservationModel) were deleted in PR-D-Pacman together with the state_transition_model / observation_model factory methods; the env-level methods construct the native kernel directly.

Run-time notes:
  • Each test that depends on reproducibility calls _native.set_seed(...) at the top of the test, because each _native.so owns its own per-module RNG singleton and the test suite does not share a single source of randomness with numpy.

class POMDPPlanners.tests.test_environments.test_pacman_native.test_pacman_native_equivalence.TestAggressiveDistribution[source]

Bases: object

test_aggressive_ghost_empirical_matches_probability()[source]

Test empirical sample distribution matches transition_log_probability.

Return type:

None

Purpose: Validates that the softmax-sampled ghost move under the

aggressive strategy in C++ produces frequencies that match the analytic transition_log_probability evaluation for the same transition.

Given: A 5x5 env with 1 aggressive ghost and no walls; seeded 0. When: 20_000 samples are drawn via env.sample_next_state; the

empirical per-next-state frequency is compared against np.exp(env.transition_log_probability(state, action, unique_states)).

Then: max |freq - prob| < 0.02 across the support.

Test type: integration

class POMDPPlanners.tests.test_environments.test_pacman_native.test_pacman_native_equivalence.TestCollisionTerminal[source]

Bases: object

test_pacman_walking_into_ghost_sets_terminal()[source]

Test that stepping onto a ghost sets the terminal flag.

Purpose: Validates the post-move collision check.

Return type:

None

Given: PacMan at (3, 2) with a ghost at (3, 3). Action east moves

PacMan to (3, 3) — the ghost may move away, but we repeat the test with a ghost the env is forced to stay in place: use the patrol strategy with an initial direction that blocks movement. For the simpler invariant here we seed many times and check at least one rollout yields a collision to terminal.

When: env.sample_next_state is called up to 50 times with different

seeds until a transition produces a collision.

Then: At least one sample lands on terminal=True.

Test type: unit

class POMDPPlanners.tests.test_environments.test_pacman_native.test_pacman_native_equivalence.TestNativeSampleAgainstBatchSample[source]

Bases: object

test_per_call_matches_batch_under_shared_seed()[source]

Test per-particle native sample() and batch_sample() agree row-by-row.

Return type:

None

Purpose: Validates that the single-instance and batch entry points of

PacManTransitionCpp draw from the same RNG stream in the same order, so bearing the same seed they produce identical outputs.

Given: A seeded native RNG and a batch of particles. The batch contains

5 copies of the initial state. The kernel is constructed directly via the env’s cached ctor kwargs.

When: batch_sample is called on the 5-row batch via env.sample_next_state_batch,

and in a separate seeded run env.sample_next_state is called 5 times in a row on the same state.

Then: The two sequences of 5 ndarrays are equal row-for-row.

Test type: integration

class POMDPPlanners.tests.test_environments.test_pacman_native.test_pacman_native_equivalence.TestPelletCollection[source]

Bases: object

test_moving_onto_pellet_flips_mask_and_increments_score()[source]

Test that PacMan moving onto an active pellet collects it.

Return type:

None

Purpose: Validates the collection / score-update branch of the

transition kernel.

Given: PacMan at (1, 0) with all 4 pellets active and score 0. Action

east moves PacMan to (1, 1) which is a registered pellet position.

When: env.sample_next_state is called once. Then: Pellet index 0 (the (1,1) pellet) flips from 1.0 to 0.0, and

the score increases by exactly env.pellet_reward.

Test type: unit

class POMDPPlanners.tests.test_environments.test_pacman_native.test_pacman_native_equivalence.TestTerminalAbsorbing[source]

Bases: object

test_terminal_state_is_absorbing()[source]

Test that sampling from a terminal state returns the state unchanged.

Return type:

None

Purpose: Validates the if terminal return state fast path in

apply_transition — terminal states are absorbing.

Given: A terminal state with terminal flag = 1.0. When: env.sample_next_state is called. Then: The returned state array equals the input byte-for-byte.

Test type: unit

class POMDPPlanners.tests.test_environments.test_pacman_native.test_pacman_native_equivalence.TestWinCondition[source]

Bases: object

test_collecting_last_pellet_sets_terminal()[source]

Test that collecting the last remaining pellet sets terminal=True.

Purpose: Validates the “no pellets remaining” terminal rule.

Given: PacMan at (1, 0) with only one pellet left at (1, 1). When: Action east moves PacMan onto (1, 1). Then: The next state has no active pellets and terminal=True.

Test type: unit

Return type:

None

POMDPPlanners.tests.test_environments.test_pacman_native.test_pacman_native_equivalence.test_scalar_obs_log_prob_un_floored_matches_batch_after_fix()[source]

Scalar obs log-prob below -690 floor matches the batch path post-fix.

Return type:

None

Purpose: Pins the post-fix contract for PacManPOMDP that

observation_log_probability (scalar) and observation_log_probability_per_state (batch) agree on a moderate-density anchor whose analytic log-probability is well below the old log(p + 1e-300) -690.776 floor but still above the kernel’s internal float64 underflow threshold. Pre-fix, the scalar path floored such values at ~-690.776 while the batch path returned the un-floored kernel log-likelihood — the asymmetry that motivated the env-wide log-prob floor removal.

Given: The shared 2-ghost env from _build_env, a fresh

initial state, action 0, and a 2-D ndarray observation [[31, 31, 31, 31]] (one row of 2*num_ghosts coordinates). At this offset the analytic 4-D Gaussian log-pdf for both ghosts is ≈ -710.187.

When: Both observation_log_probability (with the 2-D ndarray

fast path) and observation_log_probability_per_state are evaluated on the same (next_state, action, observation).

Then: Both return finite, equal values to within atol=1e-6, and

the common value is below -700 (past the old floor).

Test type: unit