Metadata-Version: 2.4
Name: planning-machine
Version: 0.3.7
Home-page: https://github.com/mmd-u3fi-lm/planning-machine
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: unified_planning>=1.3.0
Requires-Dist: PyYAML>=6.0.3
Requires-Dist: docker>=7.1.0
Requires-Dist: osmnx>=2.0.7
Requires-Dist: pillow>=12.1.0
Requires-Dist: mcp>=1.26.0
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-dist

# Introduction
PlanningMachine is an automated planning library that parse YAML-based instructions into PDDL, solves them using a planner, and returns a structured plan with step effects. It can be run as a standalone service via Docker Compose or integrated directly into Python projects as a library. The goal of this project is to make the technology in automated planning more accessible to software engineers without exposing them to the internals of how they work.

# Setup and Installation
The Python library can be installed either by building the wheel files from source or via pip. We recommend the latter. 

## from Source
1. In the root directory, execute `python setup.py sdist bdist_wheel` to create a `.whl` file in a newly created `dist` folder.
2. Execute `pip install /dist/filename.whl`, where `filename.whl` is a placeholder name for the created `.whl` file.

## from PyPi
Execute `pip install planning_machine`.

# Usage

## Config file
PlanningMachine is driven by a `config.yaml` file that specifies:

- `domain`: relative path to a `domain.yaml` file (required)
- `problem`: relative path to a `problem.yaml` file (required)
- `planner`: planner name (optional, default `LAPKT`)
- `mem_limit`: memory limit in GB (optional, default `8`)
- `cpu_limit`: CPU limit in cores (optional, default `1`)

Relative paths are resolved against the directory containing `config.yaml`. Example:

```yaml
domain: domain.yaml
problem: problem.yaml
cpu_limit: 1
mem_limit: 8
planner: LAPKT
```

## Grammar overview
`config.yaml` instructs PlanningMachine to read two YAML files. At a high level:

A **domain file** describes the "rules of the world" and contains four top-level keys: `domain` (a name), `types` (a hierarchy mapping each subtype to its supertype), `predicates` (the relations that can hold, with typed arguments), and `actions`. Each action defines its `parameters`, a `precondition` that must hold for it to apply, and the `effect` it produces.

A **problem file** describes a specific scenario to solve and contains five top-level keys: `problem` (a name), `domain` (the name of the domain it uses), `objects` (the concrete things in the world, grouped by type), `init` (the predicates true in the starting state), and `goal` (the predicates that must be true in a solution).

Preconditions, effects, and goals are written as **formulas**: a single atom such as `at: [veh, loc]`, a negated atom using `not`, or an `and`/`or` junction over a list of atoms. Effects may additionally be **conditional**, using a `when` block with `condition` and `then` lists. All names (types, predicates, actions, parameters, and objects) are identifiers starting with a letter or underscore. The formal syntax rules for the `domain.yaml` and `problem.yaml` files are documented in the `docs/grammar.md` file in the source code.

### Example
A minimal logistics domain with a single `load` action:

```yaml
domain: logistics
types:
  # vehicle (subtype) is locatable (type)
  vehicle: locatable    
  package: locatable
  loc: location
predicates:
    # at(obj: locatable, loc: location)
    at:     
    - obj: locatable
    - loc: location
    # in(pak: package, veh: vehicle)
    in:     
        - pak: package
        - veh: vehicle
actions:
  # loads a package into a vehicle if and only if they are in the same location.
  load:
    # load(pak: package, veh: vehicle, loc: location)
    parameters: 
      - pak: package
      - veh: vehicle
      - loc: location
    # precondition is equivalent to: ```if at(veh, loc) && at(pak, loc)``` 
    precondition:
      and:
        - at: [veh, loc]
        - at: [pak, loc]
    # effect is equivalent to the "then" part of an "if":  in(pak, veh) = True && at(pak, loc) = False
    effect:
      and:
        - in: [pak, veh]
        - not: {at: [pak, loc]}
```

A matching problem that asks for the laptop to end up loaded in the car:

```yaml
problem: load_laptop
domain: logistics
objects:
  vehicle: [car]
  package: [laptop]
  location: [warehouse]
init:
  at:
    - [car, warehouse]
    - [laptop, warehouse]
goal:
  in: [laptop, car]
```

Solving this problem yields a single-step plan: `load(laptop, car, warehouse)`.


## Docker Compose (Only Works with the Source Code)
1. Create a directory named `workdir` in the root folder.
2. Place `config.yaml`, `domain.yaml`, and `problem.yaml` in `workdir`.
3. Execute `docker compose up` in the root folder. This parses the files in `workdir` to PDDL, calls the planner to solve them, and extracts the effects of each plan step. The solution is placed in `workdir/plan.yaml`.

## Python

Import and use the `PlanningMachine` class as follows:

```python
from planning_machine import PlanningMachine

pm = PlanningMachine()
solution = pm.solve(config_path)
```

Provide the path to your `config.yaml`, and `.solve()` will return the solution to your planning problem. By default, PlanningMachine runs via Docker, where more planners are available. To disable this, pass `dockerized=False` when instantiating the class: `pm = PlanningMachine(dockerized=False)`. Note that this reverts to the default available planner, ignoring the one requested in `config.yaml`.

A demo Jupyter notebook, `demo.ipynb`, is also included in the source files. It walks through a real-world logistics problem end-to-end, covering setup, solving, analytics, and visualization. Alternatively, you can run the demo with the following, which creates an `output` directory containing the visualizations and the solution:

```python
from planning_machine import PlanningMachine

pm = PlanningMachine()
pm.demo()
```

## Contact
Please contact <a href="mailto:opensource@learningmachines.au">opensource@learningmachines.au</a> for questions, comments, or feedback about the PlanningMachine library.
