POMDPPlanners.planners.vectorized_planners.vopp package
Vectorized Online POMDP Planner (VOPP / PORPP).
This package hosts the VOPP planner and its episode runner, whose whole search
is expressed as batched tensor operations over the flat-tensor belief tree in
POMDPPlanners.core.tree.vectorized_belief_tree.
Provides:
VOPPPlanner– the Vectorized Online POMDP Planner (VOPP / PORPP).VOPPEpisodeRunner– drives full episodes with the VOPP planner.
- class POMDPPlanners.planners.vectorized_planners.vopp.VOPPEpisodeRunner(planner, model, *, num_belief_particles=1000, max_steps=50, world_transition=None, world_observation=None)[source]
Bases:
objectRuns closed-loop POMDP episodes with a
VOPPPlanner.The runner owns the interaction loop but no planning policy of its own: the planner decides actions, the vectorized model supplies the dynamics and the belief filter, and an optional pair of world hooks overrides the ground-truth transition / observation when a real simulator is the world.
- Parameters:
planner (VOPPPlanner)
model (VectorizedGenerativeModel)
num_belief_particles (int)
max_steps (int)
world_transition (Callable[[Tensor, Tensor], Tensor] | None)
world_observation (Callable[[Tensor, Tensor], Tensor] | None)
- num_belief_particles
Size of the particle belief carried between steps.
- max_steps
Maximum number of actions per episode.
Example
>>> import torch >>> from POMDPPlanners.environments.light_dark_pomdp.continuous_light_dark_pomdp import ( ... ContinuousLightDarkPOMDP, ... ) >>> from POMDPPlanners.environments.light_dark_pomdp.continuous_light_dark_vectorized_model import ( ... ContinuousLightDarkVectorizedModel, ... ) >>> from POMDPPlanners.planners.vectorized_planners import ( ... VOPPEpisodeRunner, ... VOPPPlanner, ... ) >>> _ = torch.manual_seed(0) >>> env = ContinuousLightDarkPOMDP(discount_factor=0.95, is_obstacle_hit_terminal=False) >>> model = ContinuousLightDarkVectorizedModel(env, device=torch.device("cpu")) >>> planner = VOPPPlanner( ... model, num_actions=model.num_actions, num_particles=128, ... max_depth=6, num_planning_iterations=8, ... ) >>> runner = VOPPEpisodeRunner(planner, model, num_belief_particles=256, max_steps=20) >>> initial = torch.tensor([[0.0, 5.0]]) >>> result = runner.run_episode(initial) >>> result.num_steps >= 1 True
- run_episode(initial_state, initial_particles=None)[source]
Run one closed-loop episode and return its recorded trajectory.
- Parameters:
initial_state (
Tensor) –[1, ds](or[ds]) ground-truth start state.initial_particles (
Optional[Tensor]) – Optional[num_particles, ds]initial belief; defaults to the start state replicated across the particle set.
- Return type:
- Returns:
A
VOPPEpisodeResultholding the states, beliefs, actions, rewards, planning times, and root visit counts of the episode.- Raises:
ValueError – If
initial_statedoes not describe a single state.
- class POMDPPlanners.planners.vectorized_planners.vopp.VOPPPlanner(model, num_actions, *, temperature=2.0, num_particles=1000, max_depth=10, discount_factor=0.95, num_planning_iterations=100, value_heuristic=None, belief_capacity=1024, action_capacity=1024, value_dtype=torch.float32)[source]
Bases:
objectFully vectorized online POMDP planner (VOPP / PORPP).
The planner owns a
VectorizedBeliefTreeand repeatedly expands and backs it up entirely with batched tensor operations. A single call toplan()samples particles from the supplied root belief, runs the configured number of forward-search / preference-backup iterations, and returns the greedy root action.- Parameters:
- device
Device every tensor lives on (taken from the model).
- num_actions
Size of the fixed representative action set.
Example
See the module-level docstring for a runnable example.
- plan(root_particles)[source]
Plan from a particle-set root belief and return the greedy action.
The tree is cleared, then
num_planning_iterationsforward-search / preference-backup passes refine the root action preferences. The action with the highest root preference is returned.- Parameters:
root_particles (
Tensor) –[num_root_particles, ds]states representing the current belief; iterations resamplenum_particlesof them with replacement.- Return type:
- Returns:
The index of the greedy root action in
[0, num_actions).- Raises:
ValueError – If
root_particlesis not a 2-D tensor on the planner’s device.
- property tree: VectorizedBeliefTree
The belief tree built by the most recent
plan()call.
- tree_metrics()[source]
Root-tree analysis metrics for the most recent
plan()call.Returns the same
TreeMetricsset the MCTS planners report (root action visit min / max / entropy, number of root actions, root visit count, max depth, leaf flag), making VOPP directly comparable to them.- Return type:
- Returns:
A list of
PolicyInfoVariabledescribing the current tree.
Submodules
POMDPPlanners.planners.vectorized_planners.vopp.vopp module
Vectorized Online POMDP Planner (VOPP / PORPP).
This module implements VOPPPlanner, a fully GPU-vectorized online
POMDP solver following Hoerger, Sudrajat and Kurniawati, Vectorized Online
POMDP Planning (arXiv:2510.27191). VOPP is the vectorized realization of
Partially Observable Reference Policy Programming (PORPP): rather than
interleaving numerical optimization with expectation estimation, it solves the
value function analytically (a log-sum-exp over action preferences) and
leaves only expectations to Monte Carlo.
The planner drives three collaborating pieces, none of which contains any planning-policy logic itself:
a
VectorizedBeliefTreeholding the tree topology, per-action visit/reward statistics, and two registered belief fields –preferences(the|A|-vectorPsiof eq. 4) andvalue(the scalar belief valueV);a
VectorizedGenerativeModelsupplying the batched transition / observation / reward / terminal / key kernels; anda root belief represented as a tensor of state particles.
Each planning iteration runs two fully vectorized passes over the tree:
Forward search (Algorithm 2): from the root belief, sample
n_pstates, and repeatedly – for every live episode in parallel – sample an action fromsoftmax(eta * Psi), push it through the generative model, accumulate the immediate reward on the action node, drop terminated episodes, and append the successor belief nodes. This descends one tree level per step untilmax_depthis exceeded, where a value heuristic scores the leaf states.Preference backup (Algorithm 3): from the deepest level up to the root, compute per-action
Q = mean_reward + gamma * weighted_future_valueand apply the PORPP preference update (eq. 5)Psi <- Psi - L_eta[Psi] + Qon every belief, recomputing each belief’s valueV = L_eta[Psi]as it goes.
After the configured budget of iterations the planner returns the root action with the highest preference.
VOPP assumes a fixed, finite representative action set indexed
0 .. num_actions - 1; the model’s action_keys must return exactly that
integer index so it can address a column of the dense preferences field.
References
Hoerger, M., Sudrajat, M., & Kurniawati, H. (2026). Vectorized Online POMDP Planning. arXiv:2510.27191. https://arxiv.org/abs/2510.27191
Example
Planning one step in Continuous Light-Dark:
>>> import torch
>>> from POMDPPlanners.environments.light_dark_pomdp.continuous_light_dark_pomdp import (
... ContinuousLightDarkPOMDP,
... )
>>> from POMDPPlanners.environments.light_dark_pomdp.continuous_light_dark_vectorized_model import (
... ContinuousLightDarkVectorizedModel,
... )
>>> from POMDPPlanners.planners.vectorized_planners import VOPPPlanner
>>> _ = torch.manual_seed(0)
>>> env = ContinuousLightDarkPOMDP(discount_factor=0.95, is_obstacle_hit_terminal=False)
>>> model = ContinuousLightDarkVectorizedModel(env, device=torch.device("cpu"))
>>> planner = VOPPPlanner(
... model,
... num_actions=model.num_actions,
... num_particles=256,
... max_depth=5,
... num_planning_iterations=8,
... discount_factor=0.95,
... )
>>> root_particles = torch.tensor([[2.0, 2.0]]).repeat(256, 1)
>>> action = planner.plan(root_particles)
>>> 0 <= action < model.num_actions
True
- class POMDPPlanners.planners.vectorized_planners.vopp.vopp.VOPPPlanner(model, num_actions, *, temperature=2.0, num_particles=1000, max_depth=10, discount_factor=0.95, num_planning_iterations=100, value_heuristic=None, belief_capacity=1024, action_capacity=1024, value_dtype=torch.float32)[source]
Bases:
objectFully vectorized online POMDP planner (VOPP / PORPP).
The planner owns a
VectorizedBeliefTreeand repeatedly expands and backs it up entirely with batched tensor operations. A single call toplan()samples particles from the supplied root belief, runs the configured number of forward-search / preference-backup iterations, and returns the greedy root action.- Parameters:
- device
Device every tensor lives on (taken from the model).
- num_actions
Size of the fixed representative action set.
Example
See the module-level docstring for a runnable example.
- plan(root_particles)[source]
Plan from a particle-set root belief and return the greedy action.
The tree is cleared, then
num_planning_iterationsforward-search / preference-backup passes refine the root action preferences. The action with the highest root preference is returned.- Parameters:
root_particles (
Tensor) –[num_root_particles, ds]states representing the current belief; iterations resamplenum_particlesof them with replacement.- Return type:
- Returns:
The index of the greedy root action in
[0, num_actions).- Raises:
ValueError – If
root_particlesis not a 2-D tensor on the planner’s device.
- property tree: VectorizedBeliefTree
The belief tree built by the most recent
plan()call.
- tree_metrics()[source]
Root-tree analysis metrics for the most recent
plan()call.Returns the same
TreeMetricsset the MCTS planners report (root action visit min / max / entropy, number of root actions, root visit count, max depth, leaf flag), making VOPP directly comparable to them.- Return type:
- Returns:
A list of
PolicyInfoVariabledescribing the current tree.
POMDPPlanners.planners.vectorized_planners.vopp.vopp_episode_runner module
Closed-loop episode driver for the vectorized planner (VOPP / PORPP).
VOPPPlanner only answers a single question – plan(root_particles)
returns one greedy action index – so on its own it cannot run a POMDP
episode: there is no ground-truth world stepping forward, no belief filter
threading information between steps, and no record of the trajectory. This
module adds exactly that thin layer.
VOPPEpisodeRunner drives a full closed loop entirely on-device:
plan an action from the current particle belief with the wrapped
VOPPPlanner(timed with a CUDA sync so the wall clock is real);step a ground-truth world state forward and draw an observation;
run a sequential-importance-resampling (SIR) particle filter – propagate the belief particles through the model transition, weight them by the model observation likelihood, and resample – to obtain the next belief.
The world is, by default, the same VectorizedGenerativeModel the
planner searches (a faithful “model-is-world” rollout, justified for
Continuous Light-Dark by its native-parity test). A caller with a different
ground-truth simulator – a real CARLA or Isaac world – injects it through the
optional world_transition / world_observation hooks while the belief
filter keeps using the vectorized model.
Every quantity the definition-of-done analysis needs is recorded per step: the true state, the belief particle cloud, the chosen action, the immediate reward, the planning wall-clock time, and the planner’s tree metrics.
- class POMDPPlanners.planners.vectorized_planners.vopp.vopp_episode_runner.VOPPEpisodeResult(states=<factory>, beliefs=<factory>, action_indices=<factory>, rewards=<factory>, plan_times=<factory>, root_visit_counts=<factory>, reached_goal=False, num_steps=0)[source]
Bases:
objectRecorded trajectory and per-step statistics of one VOPP episode.
- Parameters:
- states
[ds]true states, one per visited step plus the final state.
- beliefs
Belief particle clouds
[num_particles, ds], one per step.
- action_indices
Greedy action index chosen at each step.
- rewards
Immediate reward collected at each step.
- plan_times
Wall-clock seconds spent inside each
plan()call.
- root_visit_counts
Planner root visit count (forward-search particle simulations) backing each step’s action.
- reached_goal
Whether the world reached a terminal (goal) state.
- num_steps
Number of executed actions.
- class POMDPPlanners.planners.vectorized_planners.vopp.vopp_episode_runner.VOPPEpisodeRunner(planner, model, *, num_belief_particles=1000, max_steps=50, world_transition=None, world_observation=None)[source]
Bases:
objectRuns closed-loop POMDP episodes with a
VOPPPlanner.The runner owns the interaction loop but no planning policy of its own: the planner decides actions, the vectorized model supplies the dynamics and the belief filter, and an optional pair of world hooks overrides the ground-truth transition / observation when a real simulator is the world.
- Parameters:
planner (VOPPPlanner)
model (VectorizedGenerativeModel)
num_belief_particles (int)
max_steps (int)
world_transition (Callable[[Tensor, Tensor], Tensor] | None)
world_observation (Callable[[Tensor, Tensor], Tensor] | None)
- num_belief_particles
Size of the particle belief carried between steps.
- max_steps
Maximum number of actions per episode.
Example
>>> import torch >>> from POMDPPlanners.environments.light_dark_pomdp.continuous_light_dark_pomdp import ( ... ContinuousLightDarkPOMDP, ... ) >>> from POMDPPlanners.environments.light_dark_pomdp.continuous_light_dark_vectorized_model import ( ... ContinuousLightDarkVectorizedModel, ... ) >>> from POMDPPlanners.planners.vectorized_planners import ( ... VOPPEpisodeRunner, ... VOPPPlanner, ... ) >>> _ = torch.manual_seed(0) >>> env = ContinuousLightDarkPOMDP(discount_factor=0.95, is_obstacle_hit_terminal=False) >>> model = ContinuousLightDarkVectorizedModel(env, device=torch.device("cpu")) >>> planner = VOPPPlanner( ... model, num_actions=model.num_actions, num_particles=128, ... max_depth=6, num_planning_iterations=8, ... ) >>> runner = VOPPEpisodeRunner(planner, model, num_belief_particles=256, max_steps=20) >>> initial = torch.tensor([[0.0, 5.0]]) >>> result = runner.run_episode(initial) >>> result.num_steps >= 1 True
- run_episode(initial_state, initial_particles=None)[source]
Run one closed-loop episode and return its recorded trajectory.
- Parameters:
initial_state (
Tensor) –[1, ds](or[ds]) ground-truth start state.initial_particles (
Optional[Tensor]) – Optional[num_particles, ds]initial belief; defaults to the start state replicated across the particle set.
- Return type:
- Returns:
A
VOPPEpisodeResultholding the states, beliefs, actions, rewards, planning times, and root visit counts of the episode.- Raises:
ValueError – If
initial_statedoes not describe a single state.