Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,18 @@ pddlpy validate domain.pddl problem.pddl # diagnostics (see below)
```

`solve` defaults to `astar`; `--planner` accepts any registered planner
(`bfs`, `astar`, `gbfs`, `ucs`). Exit codes: `0` success, `1` search
(`bfs`, `astar`, `gbfs`, `ucs`, `temporal`). With `--planner temporal` the
JSON carries the schedule (#119): each step gains `start`, `duration` and
`end`, and the result a top-level `makespan`:

```bash
$ pddlpy solve weld-domain.pddl weld-problem.pddl --planner temporal
{"planner": "temporal", "solved": true, "cost": 6.0, "makespan": 6.0,
"length": 1, "steps": [{"action": "steady-weld", "args": {"?p": "p1"},
"start": 0.0, "duration": 6.0, "end": 6.0}]}
```

Exit codes: `0` success, `1` search
completed without finding a plan / `validate` found issues, `2` bad input
(missing file, unknown operator, unsupported `:requirements`).

Expand Down
4 changes: 3 additions & 1 deletion docs/object-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ the `:durative-actions` capability and returns a `TemporalPlan`.
steps, so mixed domains work. Returns a makespan-minimal sequential schedule.
- **`TemporalPlan`** — a `Plan` whose `steps` are `ScheduledAction`s
(`action`, `start`, `duration`, `end`); its `makespan` doubles as the plan
`cost`.
`cost`. The CLI and MCP `solve` render the schedule too (#119): each JSON
step carries `start`/`duration`/`end` and the result a top-level
`makespan` alongside `cost`.

```python
from pddlpy import DomainProblem
Expand Down
26 changes: 24 additions & 2 deletions pddlpy/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from typing import Any, Dict, Iterable, List, Optional

from pddlpy.pddl import DomainProblem, Operator
from pddlpy.planning import Plan, atom_tuple
from pddlpy.planning import Plan, TemporalPlan, atom_tuple


def atoms_list(atoms: Iterable[Any]) -> List[List[str]]:
Expand Down Expand Up @@ -59,9 +59,31 @@ def operator_dict(op: Operator) -> Dict[str, Any]:


def plan_dict(plan: Optional[Plan]) -> Dict[str, Any]:
"""A plan (or the absence of one, when ``plan`` is ``None``)."""
"""A plan (or the absence of one, when ``plan`` is ``None``).

A :class:`TemporalPlan` (#84) additionally carries its schedule (#119):
each step gains ``start``, ``duration`` and ``end``, and the dict gains a
top-level ``makespan`` (which also remains the ``cost``).
"""
if plan is None:
return {"solved": False, "cost": None, "length": None, "steps": None}
if isinstance(plan, TemporalPlan):
return {
"solved": True,
"cost": plan.cost,
"makespan": plan.makespan,
"length": len(plan),
"steps": [
{
"action": s.action.operator_name,
"args": dict(s.action.variable_list),
"start": s.start,
"duration": s.duration,
"end": s.end,
}
for s in plan.steps
],
}
return {
"solved": True,
"cost": plan.cost,
Expand Down
14 changes: 14 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,20 @@ def test_solve_cost_aware(capsys):
assert out["solved"] is True and out["cost"] > 0


def test_solve_temporal_schedule(capsys):
# #119: TemporalPlan steps carry the schedule, plus a top-level makespan.
code, out, _ = _run(
capsys, "solve", *_files("temporal-weld"), "--planner", "temporal"
)
assert code == 0
assert out["solved"] is True
assert out["makespan"] == out["cost"] == 6.0
step = out["steps"][0]
assert set(step) == {"action", "args", "start", "duration", "end"}
assert step["action"] == "steady-weld" and step["args"] == {"?p": "p1"}
assert step["start"] == 0.0 and step["duration"] == 6.0 and step["end"] == 6.0


def test_solve_no_plan(capsys, tmp_path):
domain = tmp_path / "d.pddl"
problem = tmp_path / "p.pddl"
Expand Down
8 changes: 8 additions & 0 deletions tests/test_mcpserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ def test_solve_default_planner():
assert out["length"] == len(out["steps"]) > 0


def test_solve_temporal_schedule():
# #119: the solve tool surfaces TemporalPlan schedules.
out = solve(*_files("temporal-weld"), planner="temporal")
assert out["solved"] is True and out["makespan"] == out["cost"]
step = out["steps"][0]
assert step["end"] == step["start"] + step["duration"]


def test_solve_no_plan(tmp_path):
domain = tmp_path / "d.pddl"
problem = tmp_path / "p.pddl"
Expand Down