diff --git a/README.md b/README.md index c2f54c5..0a4ed40 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,128 @@ # Online Learning of Action Models for PDDL Planning +
- + - + - -[//]: # ( ) - -[//]: # ( )
-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}, @@ -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) \ No newline at end of file + +Refactored with the help of [Ejdis Gjinika](https://github.com/ejdisgjinika). diff --git a/olam/OLAM.py b/olam/OLAM.py index 35e1573..90d4b53 100644 --- a/olam/OLAM.py +++ b/olam/OLAM.py @@ -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) """ diff --git a/olam/learning/__pycache__/__init__.cpython-310.pyc b/olam/learning/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index a2a2087..0000000 Binary files a/olam/learning/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/olam/learning/__pycache__/action_generator.cpython-310.pyc b/olam/learning/__pycache__/action_generator.cpython-310.pyc deleted file mode 100644 index d4c1705..0000000 Binary files a/olam/learning/__pycache__/action_generator.cpython-310.pyc and /dev/null differ diff --git a/olam/learning/__pycache__/goal_generator.cpython-310.pyc b/olam/learning/__pycache__/goal_generator.cpython-310.pyc deleted file mode 100644 index be45f5d..0000000 Binary files a/olam/learning/__pycache__/goal_generator.cpython-310.pyc and /dev/null differ diff --git a/olam/learning/__pycache__/learner.cpython-310.pyc b/olam/learning/__pycache__/learner.cpython-310.pyc deleted file mode 100644 index 8c7889f..0000000 Binary files a/olam/learning/__pycache__/learner.cpython-310.pyc and /dev/null differ diff --git a/olam/modeling/__pycache__/PDDLenv.cpython-310.pyc b/olam/modeling/__pycache__/PDDLenv.cpython-310.pyc deleted file mode 100644 index 1662451..0000000 Binary files a/olam/modeling/__pycache__/PDDLenv.cpython-310.pyc and /dev/null differ diff --git a/olam/modeling/__pycache__/__init__.cpython-310.pyc b/olam/modeling/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 4755c21..0000000 Binary files a/olam/modeling/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/olam/modeling/__pycache__/symbolic_observation.cpython-310.pyc b/olam/modeling/__pycache__/symbolic_observation.cpython-310.pyc deleted file mode 100644 index 372e7b7..0000000 Binary files a/olam/modeling/__pycache__/symbolic_observation.cpython-310.pyc and /dev/null differ diff --git a/olam/modeling/__pycache__/trajectory.cpython-310.pyc b/olam/modeling/__pycache__/trajectory.cpython-310.pyc deleted file mode 100644 index acf95b9..0000000 Binary files a/olam/modeling/__pycache__/trajectory.cpython-310.pyc and /dev/null differ diff --git a/olam/modeling/trajectory.py b/olam/modeling/trajectory.py index 9ce96c7..a57a96c 100644 --- a/olam/modeling/trajectory.py +++ b/olam/modeling/trajectory.py @@ -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): diff --git a/olam/util/__pycache__/__init__.cpython-310.pyc b/olam/util/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 5ff0076..0000000 Binary files a/olam/util/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/olam/util/__pycache__/ground_actions_util.cpython-310.pyc b/olam/util/__pycache__/ground_actions_util.cpython-310.pyc deleted file mode 100644 index 802eacd..0000000 Binary files a/olam/util/__pycache__/ground_actions_util.cpython-310.pyc and /dev/null differ diff --git a/olam/util/__pycache__/state_util.cpython-310.pyc b/olam/util/__pycache__/state_util.cpython-310.pyc deleted file mode 100644 index c7b5fb3..0000000 Binary files a/olam/util/__pycache__/state_util.cpython-310.pyc and /dev/null differ diff --git a/olam/util/__pycache__/util.cpython-310.pyc b/olam/util/__pycache__/util.cpython-310.pyc deleted file mode 100644 index 47852a8..0000000 Binary files a/olam/util/__pycache__/util.cpython-310.pyc and /dev/null differ diff --git a/olam/util/executability/__pycache__/__init__.cpython-310.pyc b/olam/util/executability/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 1f43613..0000000 Binary files a/olam/util/executability/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/pyproject.toml b/pyproject.toml index 432fb6c..76586de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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 = [