pomdp_py.utils.debugging module¶
This module contains utility functions making it easier to debug POMDP planning.
TreeDebugger¶
The core debugging functionality for POMCP/POUCT search trees is incorporated
into the TreeDebugger. It is designed for ease of use during a pdb
or
ipdb
debugging session. Here is a minimal example usage:
from pomdp_py.utils import TreeDebugger
from pomdp_problems.tiger import TigerProblem
# pomdp_py.Agent
agent = TigerProblem.create("tiger-left", 0.5, 0.15).agent
# suppose pouct is a pomdp_py.POUCT object (POMCP works too)
pouct = pomdp_py.POUCT(max_depth=4, discount_factor=0.95,
num_sims=4096, exploration_const=200,
rollout_policy=tiger_problem.agent.policy_model)
action = pouct.plan(agent)
dd = TreeDebugger(agent.tree)
import pdb; pdb.set_trace()
When the program executes, you enter the pdb debugger, and you can:
(Pdb) dd.pp
_VNodePP(n=4095, v=-19.529)(depth=0)
├─── ₀listen⟶_QNodePP(n=4059, v=-19.529)
│ ├─── ₀tiger-left⟶_VNodePP(n=2013, v=-16.586)(depth=1)
│ │ ├─── ₀listen⟶_QNodePP(n=1883, v=-16.586)
│ │ │ ├─── ₀tiger-left⟶_VNodePP(n=1441, v=-8.300)(depth=2)
... # prints out the entire tree; Colored in terminal.
(Pdb) dd.p(1)
_VNodePP(n=4095, v=-19.529)(depth=0)
├─── ₀listen⟶_QNodePP(n=4059, v=-19.529)
│ ├─── ₀tiger-left⟶_VNodePP(n=2013, v=-16.586)(depth=1)
│ │ ├─── ₀listen⟶_QNodePP(n=1883, v=-16.586)
│ │ ├─── ₁open-left⟶_QNodePP(n=18, v=-139.847)
│ │ └─── ₂open-right⟶_QNodePP(n=112, v=-57.191)
... # prints up to depth 1
Note that the printed texts are colored in the terminal.
You can retrieve the subtree through indexing:
(Pdb) dd[0]
listen⟶_QNodePP(n=4059, v=-19.529)
- [0] tiger-left: VNode(n=2013, v=-16.586)
- [1] tiger-right: VNode(n=2044, v=-16.160)
(Pdb) dd[0][1][2]
open-right⟶_QNodePP(n=15, v=-148.634)
- [0] tiger-left: VNode(n=7, v=-20.237)
- [1] tiger-right: VNode(n=6, v=8.500)
You can obtain the currently preferred action sequence by:
(Pdb) dd.mbp
listen []
listen []
listen []
listen []
open-left []
_VNodePP(n=4095, v=-19.529)(depth=0)
├─── ₀listen⟶_QNodePP(n=4059, v=-19.529)
│ └─── ₁tiger-right⟶_VNodePP(n=2044, v=-16.160)(depth=1)
│ ├─── ₀listen⟶_QNodePP(n=1955, v=-16.160)
│ │ └─── ₁tiger-right⟶_VNodePP(n=1441, v=-8.300)(depth=2)
│ │ ├─── ₀listen⟶_QNodePP(n=947, v=-8.300)
│ │ │ └─── ₁tiger-right⟶_VNodePP(n=768, v=0.022)(depth=3)
│ │ │ ├─── ₀listen⟶_QNodePP(n=462, v=0.022)
│ │ │ │ └─── ₁tiger-right⟶_VNodePP(n=395, v=10.000)(depth=4)
│ │ │ │ ├─── ₁open-left⟶_QNodePP(n=247, v=10.000)
mbp
stands for “mark best plan”.
To explore more features, browse the list of methods in the documentation.
- class pomdp_py.utils.debugging.TreeDebugger(tree)[source]¶
Bases:
object
Helps you debug the search tree; A search tree is a tree that contains a subset of future histories, organized into QNodes (value represents Q(b,a); children are observations) and VNodes (value represents V(b); children are actions).
- property depth¶
Tree depth starts from 0 (root node only). It is the largest number of edges on a path from root to leaf.
- property d¶
alias for depth
- property num_layers¶
Returns the number of layers; It is the number of layers of nodes, which equals to depth + 1
- property nl¶
alias for num_layers
- property nn¶
Returns the total number of nodes in the tree
- property nq¶
Returns the total number of QNodes in the tree
- property nv¶
Returns the total number of VNodes in the tree
- layer(depth, as_debuggers=True)[source]¶
Returns a list of nodes at the given depth. Will only return VNodes. Warning: If depth is high, there will likely be a huge number of nodes.
- Parameters:
depth (int) – Depth of the tree
as_debuggers (bool) – True if return a list of TreeDebugger objects, one for each tree on the layer.
- property leaf¶
- property b¶
alias for back
- property root¶
The root node when first creating this TreeDebugger
- property r¶
alias for root
- property c¶
Current node of interaction
- property pp¶
print tree, with preset options
- property mbp¶
Mark Best and Print. Mark the best sequence, and then print with only the marked nodes
- property pm¶
Print marked only
- mark_sequence(seq, color='blue')[source]¶
Given a list of keys (understandable by __getitem__ in _NodePP), mark nodes (both QNode and VNode) along the path in the tree. Note this sequence starts from self.current; So self.current will also be marked.
- property clear¶
Clear the marks
- property bestseq¶
Returns a list of actions, observation sequence that have the highest value for each step. Such a sequence is “preferred”.
Also, prints out the list of preferred actions for each step into the future
- static single_node_str(node, parent_edge=None, indent=1, include_children=True)[source]¶
Returns a string for printing given a single vnode.
- static preferred_actions(root, max_depth=None)[source]¶
Print out the currently preferred actions up to given max_depth
- path(dest)[source]¶
alias for path_to; Example usage:
marking path from root to the first node on the second layer:
dd.mark(dd.path(dd.layer(2)[0]))
- path_to(dest)[source]¶
Returns a list of keys (actions / observations) that represents the path from self.current to the given node dest. Returns None if the path does not exist. Uses DFS. Can be useful for marking path to a node to a specific layer. Note that the returned path is a list of keys (i.e. edges), not nodes.