β¨ A dependency-free, pure-Python PDDL planning framework: parser, grounder, classical-to-SOTA planners, heuristics and a benchmarking harness. β¨
jupyddl used to be a thin wrapper around the Julia PDDL.jl parser. It has been
rewritten from scratch as a pure-Python framework β no Julia, no native
dependencies, just the standard library β so it is trivial to install, embed and
build on top of for research and teaching.
- π§© Hand-written PDDL parser and grounder (typing, negative preconditions,
equality, action costs, and
forall/whenconditional effects). - π Uninformed planners: BFS, DFS, Iterative Deepening.
- π§ Informed planners: Dijkstra (uniform cost), Greedy Best-First, A*, Weighted A*, IDA*, and Enforced Hill Climbing (FF-style).
- π Heuristics from classical to SOTA: blind, goal-count,
h_max,h_add, FF (h_ff), critical-pathh^m(h1/h2), and LM-cut. - βοΈ Benchmarking harness for comparative analysis with CSV export and plots.
- π§± Extensible by design: planners and heuristics live behind simple registries; add your own in a few lines.
- β Zero runtime dependencies and a comprehensive test suite.
Requires Python β₯ 3.9. Using uv (recommended):
uv venv
uv pip install -e ".[dev,viz]" # 'viz' pulls matplotlib for benchmark plotsor with plain pip:
python -m pip install -e ".[dev,viz]"# Solve a single instance
jupyddl solve pddl-examples/tsp/domain.pddl pddl-examples/tsp/problem.pddl \
--search astar --heuristic lmcut
# Compare planners over a folder of <name>/{domain,problem}.pddl instances
jupyddl benchmark pddl-examples \
--planners bfs,dijkstra,astar,gbfs,ehc --heuristic hff \
--csv results.csv --plot comparison.pngfrom jupyddl import solve, build_task, solve_task, validate_plan
# One-shot solve
result = solve("pddl-examples/tsp/domain.pddl",
"pddl-examples/tsp/problem.pddl",
search="astar", heuristic="lmcut")
print(result.solved, result.cost, result.plan_names())
# Ground once, try several configurations
task = build_task("pddl-examples/blocksworld/domain.pddl",
"pddl-examples/blocksworld/problem.pddl")
for cfg in [("astar", "lmcut"), ("gbfs", "hff"), ("bfs", None)]:
r = solve_task(task, cfg[0], cfg[1])
assert validate_plan(task, r.plan)
print(cfg, r.cost, r.stats.expanded, "expanded")| Planners | Heuristics |
|---|---|
bfs, dfs, iddfs, dijkstra, gbfs, astar, wastar, idastar, ehc |
blind, goalcount, hmax, hadd, hff, h1, h2/hm, lmcut |
A*/IDA* are cost-optimal with an admissible heuristic (blind, hmax,
h1/h2, lmcut); dijkstra and bfs are optimal without a heuristic.
STRIPS, :typing (with hierarchy), :negative-preconditions, :equality,
:action-costs ((increase (total-cost) k)), universally-quantified goals, and
forall/when conditional effects. Numeric fluents beyond total-cost and
:durative-actions are out of scope and rejected with a clear error.
Note: on domains with conditional effects (e.g.
flip), the delete-relaxation heuristics (hadd,hff,lmcut,h^m) are satisficing only β admissibility is not guaranteed. Usebfs,dijkstra, orastar/idastarwithblindfor guaranteed-optimal plans on such domains.
jupyddl/
parser/ tokenizer + AST + recursive-descent parser
grounding.py Domain+Problem -> grounded Task (typing, PNF, static pruning)
task.py grounded STRIPS(+conditional-effects) task & operators
search/ planners + shared best-first engine + registry
heuristics/ heuristics + delete-relaxation machinery + registry
benchmark.py comparative benchmarking (CSV + plots)
cli.py `jupyddl solve` / `jupyddl benchmark`
Add a planner by subclassing jupyddl.search.Planner (or reusing best_first)
and registering it in jupyddl.search.PLANNERS; add a heuristic by subclassing
jupyddl.heuristics.Heuristic and registering it in
jupyddl.heuristics.HEURISTICS.
git submodule update --init # fetch the pddl-examples used by the tests
uv pip install -e ".[dev,viz]"
pytest --cov=jupyddl # run the test suite
flake8 jupyddl tests # lint@misc{https://doi.org/10.13140/rg.2.2.22418.89282,
doi = {10.13140/RG.2.2.22418.89282},
url = {http://rgdoi.net/10.13140/RG.2.2.22418.89282},
author = {Erwin Lejeune},
title = {Jupyddl, an extensible python library for PDDL planning and parsing},
year = {2021}
}
- Erwin Lejeune
- Sampreet Sarkar