Skip to content

Commit 7ef3e80

Browse files
authored
Rewrite jupyddl as a pure-Python PDDL planning framework (remove Julia) (#148)
1 parent 8780e03 commit 7ef3e80

62 files changed

Lines changed: 3011 additions & 3158 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.flake8

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[flake8]
2+
max-line-length = 100
3+
extend-ignore = E203, W503
4+
exclude = .venv, pddl-examples, build, dist, .git, __pycache__

.github/workflows/build.yml

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,10 @@ jobs:
66
build:
77
runs-on: ${{ matrix.os }}
88
strategy:
9+
fail-fast: false
910
matrix:
1011
os: [ubuntu-latest]
11-
# Python 3.8 is the newest interpreter for which the pinned
12-
# dependencies (matplotlib==3.5.1, pyjulia==0.5.7) resolve a
13-
# compatible NumPy 1.x. Newer interpreters pull NumPy 2.x and break
14-
# matplotlib 3.5.1's ABI.
15-
python-version: ['3.8']
12+
python-version: ['3.9', '3.10', '3.11', '3.12']
1613

1714
steps:
1815
- uses: actions/checkout@v4
@@ -22,24 +19,11 @@ jobs:
2219
uses: actions/setup-python@v5
2320
with:
2421
python-version: ${{ matrix.python-version }}
25-
- uses: julia-actions/setup-julia@v2
26-
with:
27-
version: '1.5.2'
28-
- name: Install Python dependencies
22+
- name: Install package (dev extras)
2923
run: |
3024
python -m pip install --upgrade pip
31-
python -m pip install -r requirements.txt
32-
- name: Install Julia dependencies
33-
# Build PyCall against *this* Python interpreter (via pyjulia's
34-
# installer) so the in-process bridge used by jupyddl works, then add
35-
# the PDDL.jl parser fork.
36-
run: |
37-
python -c "import julia; julia.install()"
38-
julia --color=yes -e 'using Pkg; Pkg.add(Pkg.PackageSpec(url="https://github.com/APLA-Toolbox/PDDL.jl"))'
25+
python -m pip install -e ".[dev]"
3926
- name: Lint with flake8
4027
run: |
41-
python -m pip install flake8
42-
# stop the build if there are Python syntax errors or undefined names
43-
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
44-
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
45-
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
28+
flake8 jupyddl tests --count --select=E9,F63,F7,F82 --show-source --statistics
29+
flake8 jupyddl tests --count --statistics

.github/workflows/format.yml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,22 @@ jobs:
66
format:
77
runs-on: ubuntu-latest
88
steps:
9-
- uses: actions/checkout@v2
9+
- uses: actions/checkout@v4
1010
with:
1111
ref: ${{ github.head_ref }}
12-
- uses: actions/checkout@v2
13-
- name: Set up Python 3.7
14-
uses: actions/setup-python@v3
12+
- name: Set up Python
13+
uses: actions/setup-python@v5
1514
with:
16-
python-version: 3.14
15+
python-version: '3.12'
1716
- name: Install formatter dependencies
1817
run: |
1918
python -m pip install --upgrade pip
2019
python -m pip install black
2120
- name: Format with black
2221
run: |
23-
black .
22+
black jupyddl tests
2423
- name: Commit changes
25-
uses: stefanzweifel/git-auto-commit-action@v4.14.0
24+
uses: stefanzweifel/git-auto-commit-action@v5
2625
with:
2726
commit_message: Apply formatting changes
2827
branch: main

.github/workflows/tests.yml

Lines changed: 8 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,14 @@ name: tests
33
on: [push]
44

55
jobs:
6-
build:
7-
runs-on: ${{ matrix.os }}
8-
strategy:
9-
matrix:
10-
os: [ubuntu-latest]
11-
python-version: ['3.8']
12-
13-
steps:
14-
- uses: actions/checkout@v4
15-
with:
16-
submodules: recursive
17-
- name: Set up Python ${{ matrix.python-version }}
18-
uses: actions/setup-python@v5
19-
with:
20-
python-version: ${{ matrix.python-version }}
21-
- uses: julia-actions/setup-julia@v2
22-
with:
23-
version: '1.5.2'
24-
- name: Install Python dependencies
25-
run: |
26-
python -m pip install --upgrade pip
27-
python -m pip install -r requirements.txt
28-
- name: Install Julia dependencies
29-
run: |
30-
python -c "import julia; julia.install()"
31-
julia --color=yes -e 'using Pkg; Pkg.add(Pkg.PackageSpec(url="https://github.com/APLA-Toolbox/PDDL.jl"))'
32-
- name: Lint with flake8
33-
run: |
34-
python -m pip install flake8
35-
# stop the build if there are Python syntax errors or undefined names
36-
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
37-
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
38-
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
39-
406
test:
417
runs-on: ${{ matrix.os }}
428
strategy:
9+
fail-fast: false
4310
matrix:
4411
os: [ubuntu-latest]
45-
python-version: ['3.8']
12+
python-version: ['3.9', '3.10', '3.11', '3.12']
13+
4614
steps:
4715
- uses: actions/checkout@v4
4816
with:
@@ -51,29 +19,17 @@ jobs:
5119
uses: actions/setup-python@v5
5220
with:
5321
python-version: ${{ matrix.python-version }}
54-
- uses: julia-actions/setup-julia@v2
55-
with:
56-
version: '1.5.2'
57-
- name: Install Python dependencies
22+
- name: Install package (dev extras)
5823
run: |
5924
python -m pip install --upgrade pip
60-
python -m pip install -r requirements.txt
61-
python -m pip install flake8 pytest pytest-cov
62-
- name: Install Julia dependencies
63-
# Build PyCall against this Python (needed so pytest can import the
64-
# jupyddl -> PDDL.jl bridge), then add the PDDL.jl parser fork.
65-
run: |
66-
python -c "import julia; julia.install()"
67-
julia --color=yes -e 'using Pkg; Pkg.add(Pkg.PackageSpec(url="https://github.com/APLA-Toolbox/PDDL.jl"))'
25+
python -m pip install -e ".[dev]"
6826
- name: Lint with flake8
6927
run: |
70-
# stop the build if there are Python syntax errors or undefined names
71-
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
72-
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
73-
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
28+
flake8 jupyddl tests --count --select=E9,F63,F7,F82 --show-source --statistics
29+
flake8 jupyddl tests --count --statistics
7430
- name: Test with pytest
7531
run: |
76-
pytest --cov=./
32+
pytest --cov=jupyddl --cov-report=xml
7733
- name: Upload coverage to Codecov
7834
uses: codecov/codecov-action@v4
7935
with:

AGENTS.md

Lines changed: 31 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,37 @@
22

33
## Cursor Cloud specific instructions
44

5-
`jupyddl` (PythonPDDL) is a Python library + CLI for PDDL automated planning. It is a
6-
thin wrapper around the Julia `PDDL.jl` parser, bridged through `pyjulia`/`PyCall.jl`.
7-
There are no servers/databases — "running the app" means parsing PDDL domain/problem
8-
files and running the planners, either via the library or the `scripts/ipc.py` CLI.
5+
`jupyddl` is a **pure-Python** PDDL planning framework (parser, grounder,
6+
planners, heuristics, benchmarking). The Julia/`PDDL.jl`/PyCall integration has
7+
been removed — there is no Julia, no native build step, and the core has zero
8+
runtime dependencies.
99

10-
### Environment layout (baked into the VM snapshot)
11-
- Julia 1.5.2 in `/opt/julia-1.5.2` (symlinked at `/usr/local/bin/julia`).
12-
- Python 3.8 venv at `.venv`, built from the **deadsnakes** `/usr/bin/python3.8`.
13-
- Julia packages `PyCall.jl` + the `APLA-Toolbox/PDDL.jl` fork live in `~/.julia`.
14-
`PyCall` is built against `.venv/bin/python`.
15-
- `.venv`, `logs/*`, and the `pddl-examples` submodule contents are git-ignored.
10+
### Environment
11+
- Python ≥ 3.9; a `.venv` (created with `uv`, Python 3.12) with an editable
12+
install: `uv pip install -e ".[dev]"` (add `viz` for matplotlib-based
13+
benchmark plots). `.venv` and `pddl-examples/` contents are git-ignored.
14+
- The `pddl-examples` git submodule supplies the domains/problems the tests use;
15+
it must be initialised (`git submodule update --init`).
1616

17-
### Running / testing / linting
18-
Always use the venv interpreter and run from the repo root (relative `pddl-examples/...`
19-
paths and the auto-created `logs/` dir depend on CWD):
20-
- Library / hello-world: `.venv/bin/python -c "from jupyddl import AutomatedPlanner; ..."`
21-
- CLI: `cd scripts && ../.venv/bin/python ipc.py <domain.pddl> <problem.pddl> <output>`
22-
- Tests: `.venv/bin/python -m pytest --cov=./` (from repo root).
23-
- Lint (as CI): `.venv/bin/python -m flake8 . --select=E9,F63,F7,F82` is the build-gating
24-
check; the second CI `flake8` pass uses `--exit-zero` (style warnings only, non-blocking).
17+
### Running / testing / linting (use the venv interpreter)
18+
- Tests: `.venv/bin/python -m pytest` (add `--cov=jupyddl`).
19+
- Lint (as CI): `flake8 jupyddl tests` (config in `.flake8`, max-line 100).
20+
- CLI: `.venv/bin/python -m jupyddl.cli solve <domain> <problem> -s astar -H lmcut`
21+
or `... benchmark pddl-examples --csv out.csv`. Installed as `jupyddl` too.
2522

26-
### Non-obvious gotchas
27-
- **pyjulia needs a dynamically-linked Python.** The `.venv` intentionally uses the
28-
deadsnakes `python3.8` (dynamically linked). Do **not** rebuild the venv from a
29-
`uv`-managed / python-build-standalone interpreter — those are statically linked to
30-
libpython and break the in-process Julia bridge.
31-
- **If the venv is recreated at a different path, rebuild PyCall** so it points at the new
32-
interpreter: `PYTHON=/workspace/.venv/bin/python .venv/bin/python -c "import julia; julia.install()"`.
33-
- **matplotlib backend.** `jupyddl/data_analyst.py` picks `TkAgg` when `DISPLAY` is set and
34-
`Agg` otherwise. The VM has a virtual display (`DISPLAY=:1`) and `python3.8-tk` is
35-
installed, so the default import works. For a purely headless run, invoke with
36-
`env -u DISPLAY ...` (or `MPLBACKEND=Agg` when the import path allows it) to force `Agg`.
37-
- **`DISPLAY` changes the test outcome.** `DataAnalyst.__get_all_pddl_from_data` only walks
38-
the whole `pddl-examples/` folder when `DISPLAY` is set; otherwise it returns a hardcoded
39-
`dinner`-only list. Headless (no `DISPLAY`, as in CI) the full suite is green — run tests
40-
with `env -u DISPLAY .venv/bin/python -m pytest` to reproduce CI (all 77 pass). With this
41-
VM's `DISPLAY=:1`, 19 `DataAnalyst` tests fail with `PyCall.jlwrap ... 'domain' keyword is
42-
missing`: the folder walk pairs files from an unsorted `os.walk` assuming `domain.pddl`
43-
precedes `problem.pddl`, but the `pallet` example is ordered the other way, so a problem
44-
file is parsed as a domain. That is a pre-existing code bug, not an environment issue.
45-
- **CI (`.github/workflows/*.yml`) is pinned to Python 3.8 on `ubuntu-latest`.** 3.8 is the
46-
newest interpreter whose resolved deps keep NumPy at 1.x (matplotlib 3.5.1 breaks on NumPy
47-
2.x). macOS runners were dropped: they are arm64, which has no Julia 1.5.2 build and no EOL
48-
Python build. CI builds PyCall against the runner Python with `python -c "import julia;
49-
julia.install()"` before adding `PDDL.jl`.
23+
### Non-obvious notes
24+
- **Example data quirks (external submodule, do not "fix" in this repo):**
25+
`grid` uses numeric fluents and is intentionally unsupported (raises
26+
`UnsupportedFeatureError`); `vehicle` has typos in its problem file
27+
(`struck`/`truck`, `acessible`) so its goal is unreachable and it is correctly
28+
reported unsolvable. Tests treat both as expected.
29+
- **Conditional effects (`flip`)**: the delete-relaxation heuristics (`hadd`,
30+
`hff`, `lmcut`, `h^m`) are *not guaranteed admissible* on domains with
31+
conditional effects because each conditional effect is relaxed into its own
32+
operator. For guaranteed-optimal plans there use `bfs`, `dijkstra`, or
33+
`astar`/`idastar` with the `blind` heuristic. Optimality tests use these.
34+
- **Matplotlib is optional**: only `jupyddl.benchmark.plot_summary` (and
35+
`jupyddl solve/benchmark --plot`) need it; run headless with `MPLBACKEND=Agg`
36+
if no display. The test suite does not require it.
37+
- Extend via the registries: `jupyddl.search.PLANNERS` and
38+
`jupyddl.heuristics.HEURISTICS`.

CHANGELOG.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Changelog
2+
3+
All notable changes to this project are documented in this file. The format is
4+
based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this
5+
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6+
7+
## [1.0.0] - 2026-07-01
8+
9+
Complete rewrite: **the Julia dependency is removed** and the project is now a
10+
pure-Python planning framework.
11+
12+
### Added
13+
- Hand-written PDDL tokenizer, AST and recursive-descent parser
14+
(`jupyddl.parser`).
15+
- Grounder (`jupyddl.grounding`) with type hierarchies, static-predicate
16+
pruning, positive-normal-form compilation of negative preconditions/goals,
17+
object harvesting for undeclared constants, and `forall`/`when` conditional
18+
effect expansion.
19+
- Grounded task representation with conditional effects (`jupyddl.task`).
20+
- Planners (`jupyddl.search`): BFS, DFS, Iterative Deepening, Dijkstra
21+
(uniform cost), Greedy Best-First, A*, Weighted A*, IDA*, and Enforced Hill
22+
Climbing, plus a shared best-first engine and a planner registry.
23+
- Heuristics (`jupyddl.heuristics`): blind, goal-count, `h_max`, `h_add`, FF,
24+
critical-path `h^m` (`h1`/`h2`), and LM-cut, plus a heuristic registry.
25+
- Benchmarking harness (`jupyddl.benchmark`) with CSV export and comparison
26+
plots, and a CLI (`jupyddl solve` / `jupyddl benchmark`).
27+
- High-level API: `solve`, `build_task`, `solve_task`, `validate_plan`.
28+
- Comprehensive pytest suite covering parsing, grounding, search optimality,
29+
heuristic admissibility, the API, the benchmark harness and the CLI.
30+
31+
### Changed
32+
- Packaging migrated from `setup.py`/`requirements.txt` to `pyproject.toml`
33+
(hatchling); the core has **zero runtime dependencies** (matplotlib is an
34+
optional `viz` extra).
35+
- CI reworked to run on modern Python without Julia.
36+
37+
### Removed
38+
- The Julia / `PDDL.jl` / PyCall / pyjulia integration and the old
39+
`AutomatedPlanner` / `DataAnalyst` API.

0 commit comments

Comments
 (0)