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
145 changes: 107 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,128 @@
# Online Learning of Action Models for PDDL Planning

<div style="display: flex; gap: 10px;">

<a href="https://opensource.org/licenses/MIT" target="_blank">
<img src="https://img.shields.io/badge/License-MIT-green.svg" height="20"/></a>

<a href="https://pypi.python.org/pypi/olam" target="_blank">
<img src="https://badge.fury.io/py/olam.svg" height="20"/></a>

[//]: # ( <a href="https://amlgym.readthedocs.io/en/latest/" target="_blank">)

[//]: # ( <img src="https://readthedocs.org/projects/amlgym/badge/?version=latest" height="20"/></a>)

</div>

This repository contains the *refactored* code of the
Online Learning of Action Models (OLAM) algorithm presented at IJCAI 2021,
for details about the method please see
the [paper](https://www.ijcai.org/proceedings/2021/0566.pdf). The previous code used
for the paper experiments is available at this
[link](https://github.com/LamannaLeonardo/OLAM/tree/ijcai-2021).
---

Refactored implementation of the **Online Learning of Action Models (OLAM)** algorithm, presented at **IJCAI 2021** ([paper](https://www.ijcai.org/proceedings/2021/0566.pdf)).

## Example Usage
```python
import unified_planning
from unified_planning.io import PDDLReader
from unified_planning.shortcuts import SequentialSimulator
from amlgym.util.util import empty_domain
from olam.OLAM import OLAM
OLAM learns PDDL action models online — interleaving planning and execution — without requiring pre-collected trajectories. Starting from a domain skeleton (predicates and operator signatures with empty preconditions and effects), it incrementally refines the action model as the agent acts in a simulated environment.

# Disable printing of planning engine credits
unified_planning.shortcuts.get_environment().credits_stream = None
> The original code used for the paper experiments is preserved at the [`ijcai-2021`](https://github.com/LamannaLeonardo/OLAM/tree/ijcai-2021) branch.

domain_ref_path = "olam/benchmarks/domains/blocksworld.pddl"
problem_path = "olam/benchmarks/problems/blocksworld/1_p00_blocksworld_gen.pddl"
empty_domain_path = empty_domain(domain_ref_path) # remove preconditions/effects
olam = OLAM(empty_domain_path)

sim_problem = PDDLReader().parse_problem(domain_ref_path,
problem_path)
simulator = SequentialSimulator(sim_problem)
learned_domain_str, trajectory = olam.run(simulator, max_steps=100)
## Installation

print(f"Generated a trajectory with {len(trajectory.observations)} states")
print(f"Domain learned: {learned_domain_str}")
### From PyPI

```bash
pip install olam
```

### From [AMLGym](https://amlgym.readthedocs.io/en/latest/index.html)
```bash
pip install amlgym
```

## Installation for developers
Please refer to [OLAM integration in AMLGym](https://amlgym.readthedocs.io/en/latest/active_algorithms/olam.html)
for a usage example.

Clone this repository and install in developer mode:

```
### For developers

Clone the repository and install in editable mode:

```bash
git clone https://github.com/LamannaLeonardo/OLAM.git
cd OLAM
pip install -e .
```
> OLAM is integrated into [AMLGYM](https://github.com/LamannaLeonardo/OLAM/tree/ijcai-2021).


---

## Quick Start

## Citations
```python
from unified_planning.io import PDDLReader
from unified_planning.shortcuts import SequentialSimulator
from amlgym.benchmarks import get_domain_path, get_problems_path
from amlgym.util.util import empty_domain

from olam.OLAM import OLAM

# Instantiate a simulated environment from a PDDL domain and problem
domain = 'blocksworld'
domain_ref_path = get_domain_path(domain)
problem_path = get_problems_path(domain, kind='learning')[0]
problem = PDDLReader().parse_problem(domain_ref_path, problem_path)
env = SequentialSimulator(problem=problem)

# Get an input domain path with predicates and operators signature
input_domain_path = empty_domain(domain_ref_path)

# Run the OLAM algorithm
olam = OLAM(input_domain_path)
domain_learned, trajectory = olam.run(env, max_steps=100)

# Print learned domain and produced trajectory
print("##################### Learned domain #####################")
print(domain_learned)

print("################# Generated trajectory ##################")
print(trajectory)
```

## Benchmark Results
OLAM is evaluated on 23 classical planning domains drawn from the IPC benchmark suite. Each domain is paired with a set of 10 problem instances used for incremental online learning.
The following table reports for each domain the *syntactic precision and recall* of the learned model and CPU time (seconds) averaged over all instances.

| Domain | Precision | Recall | CPU Time (s) |
|---|---|---|---|
| Barman | 0.98 | 1.00 | 385.92 |
| Blocksworld | 1.00 | 1.00 | 3.57 |
| Depots | 0.98 | 1.00 | 5.52 |
| Driverlog | 0.94 | 1.00 | 7.67 |
| Elevators | 0.89 | 1.00 | 183.89 |
| Ferry | 0.93 | 1.00 | 2.07 |
| Floortile | 0.83 | 1.00 | 5.17 |
| Gold-Miner | 0.81 | 1.00 | 15.76 |
| Grid | 0.82 | 1.00 | 5.06 |
| Gripper | 1.00 | 1.00 | 0.43 |
| Hanoi | 0.88 | 1.00 | 0.39 |
| Matching-BW | 0.99 | 1.00 | 66.49 |
| Miconic | 1.00 | 1.00 | 3.99 |
| N-Puzzle | 0.88 | 1.00 | 0.36 |
| NoMystery | 0.92 | 1.00 | 1.46 |
| Parking | 0.89 | 1.00 | 2.49 |
| Rover | 0.83 | 0.88 | 48.19 |
| Satellite | 1.00 | 1.00 | 3.57 |
| Sokoban | 0.88 | 1.00 | 20.39 |
| Spanner | 0.93 | 1.00 | 5.54 |
| TPP | 0.95 | 1.00 | 366.42 |
| Transport | 0.93 | 1.00 | 5.56 |
| Zenotravel | 1.00 | 1.00 | 6.41 |

---

## Citation

If you find OLAM useful for your research, please cite the following paper:

```bibtex
@inproceedings{ijcai2021-566,
title = {Online Learning of Action Models for PDDL Planning},
author = {Lamanna, Leonardo and Saetti, Alessandro and Serafini, Luciano and Gerevini, Alfonso and Traverso, Paolo},
author = {Lamanna, Leonardo and Saetti, Alessandro and Serafini, Luciano
and Gerevini, Alfonso and Traverso, Paolo},
booktitle = {Proceedings of the Thirtieth International Joint Conference on
Artificial Intelligence, {IJCAI-21}},
publisher = {International Joint Conferences on Artificial Intelligence},
Expand All @@ -69,8 +132,14 @@ pip install -e .
}
```

---

## License
This project is licensed under the MIT License - see the [LICENSE](/LICENSE.md) file for details.

This project is licensed under the MIT License — see the [LICENSE](LICENSE.md) file for details.

---

## Acknowledgements
This code has been refactored with the help of [Ejdis Gjinika](https://github.com/ejdisgjinika)

Refactored with the help of [Ejdis Gjinika](https://github.com/ejdisgjinika).
36 changes: 21 additions & 15 deletions olam/OLAM.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,27 +52,33 @@ def __init__(self,
Example:
.. code-block:: python

from amlgym.util.util import empty_domain
from unified_planning.io import PDDLReader
from olam.OLAM import OLAM
from unified_planning.shortcuts import SequentialSimulator
import unified_planning
from amlgym.benchmarks import get_domain_path, get_problems_path
from amlgym.util.util import empty_domain

from olam.OLAM import OLAM

# Instantiate a simulated environment from a PDDL domain and problem
domain = 'blocksworld'
domain_ref_path = get_domain_path(domain)
problem_path = get_problems_path(domain, kind='learning')[0]
problem = PDDLReader().parse_problem(domain_ref_path, problem_path)
env = SequentialSimulator(problem=problem)

# Disable printing of planning engine credits
unified_planning.shortcuts.get_environment().credits_stream = None
# Get an input domain path with predicates and operators signature
input_domain_path = empty_domain(domain_ref_path)

domain_ref_path = "olam/benchmarks/domains/blocksworld.pddl"
problem_path = "olam/benchmarks/problems/blocksworld/1_p00_blocksworld_gen.pddl"
empty_domain_path = empty_domain(domain_ref_path)
olam = OLAM(empty_domain_path)
# Run the OLAM algorithm
olam = OLAM(input_domain_path)
domain_learned, trajectory = olam.run(env, max_steps=100)

sim_problem = PDDLReader().parse_problem(domain_ref_path,
problem_path)
simulator = SequentialSimulator(sim_problem)
learned_domain_str, trajectory = olam.run(simulator, max_steps=100)
# Print learned domain and produced trajectory
print("##################### Learned domain #####################")
print(domain_learned)

print(f"Generated a trajectory with {len(trajectory.observations)} states")
print(f"Domain learned: {learned_domain_str}")
print("################# Generated trajectory ##################")
print(trajectory)

"""

Expand Down
Binary file removed olam/learning/__pycache__/__init__.cpython-310.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed olam/learning/__pycache__/learner.cpython-310.pyc
Binary file not shown.
Binary file removed olam/modeling/__pycache__/PDDLenv.cpython-310.pyc
Binary file not shown.
Binary file removed olam/modeling/__pycache__/__init__.cpython-310.pyc
Binary file not shown.
Binary file not shown.
Binary file removed olam/modeling/__pycache__/trajectory.cpython-310.pyc
Binary file not shown.
2 changes: 1 addition & 1 deletion olam/modeling/trajectory.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def __str__(self):
else:
trajectory_str += f"{literal}, "
if i < len(self.observations) - 1:
trajectory_str += f"\n (action {self.actions[i].action.name} )\n"
trajectory_str += f"\n(:action {self.actions[i]})\n"
return trajectory_str

def __repr__(self):
Expand Down
Binary file removed olam/util/__pycache__/__init__.cpython-310.pyc
Binary file not shown.
Binary file not shown.
Binary file removed olam/util/__pycache__/state_util.cpython-310.pyc
Binary file not shown.
Binary file removed olam/util/__pycache__/util.cpython-310.pyc
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ addopts = "--ignore=tests/heavy"

[project]
name = "olam"
version = "1.0.2"
version = "1.0.3"
dynamic = ["dependencies"]
requires-python = ">=3.10"
maintainers = [
Expand Down
Loading