From f730cea2870756b7ebd7ee608567704060f33702 Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Wed, 8 Nov 2023 13:59:01 +0100 Subject: [PATCH 01/19] fix(doc): Fix broken link to compiler notebook. --- docs/getting_started/quickstart.md | 2 +- docs/issue_tracking.rst | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) delete mode 100644 docs/issue_tracking.rst diff --git a/docs/getting_started/quickstart.md b/docs/getting_started/quickstart.md index b95075844..8516aed63 100644 --- a/docs/getting_started/quickstart.md +++ b/docs/getting_started/quickstart.md @@ -137,7 +137,7 @@ with PlanValidator(problem_kind=problem.kind, plan_kind=plan.kind) as validator: It is also possible to use the `Compiler` operation mode with `compilation_kind=CompilationKind.GROUNDING` to create an equivalent formulation of a problem that does not use parameters for the actions. -For an in-depth tutorial about the `Compiler` operation mode check the [Notebook on Compilers](https://colab.research.google.com/github/aiplan4eu/unified-planning/blob/master/notebooks/Compilers_example.ipynb). +For an in-depth tutorial about the `Compiler` operation mode check the [Notebook on Compilers](https://colab.research.google.com/github/aiplan4eu/unified-planning/blob/master/docs/notebooks/05-compilers.ipynb). ```python with Compiler(problem_kind=problem.kind, compilation_kind=CompilationKind.GROUNDING) as grounder: diff --git a/docs/issue_tracking.rst b/docs/issue_tracking.rst deleted file mode 100644 index befb634ac..000000000 --- a/docs/issue_tracking.rst +++ /dev/null @@ -1,4 +0,0 @@ -Issue Tracking -============== - -Issue tracking is done in GitHub issues: https://github.com/aiplan4eu/unified-planning/issues From caf0f6761a6ae130d763359c417a77a5b540f265 Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Wed, 8 Nov 2023 13:59:49 +0100 Subject: [PATCH 02/19] doc: Add contributor's guide --- docs/contributor_guide.rst | 40 ++++++++++++++++++++++++++++++++++++++ docs/index.rst | 4 ++-- 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 docs/contributor_guide.rst diff --git a/docs/contributor_guide.rst b/docs/contributor_guide.rst new file mode 100644 index 000000000..304df7f9c --- /dev/null +++ b/docs/contributor_guide.rst @@ -0,0 +1,40 @@ +Contributor's Guide +=================== + + + +Documentation +------------- + +The documentation is maintained in the `docs/` folder of the `main repository `_ +It consists of a collection of reStructuredText documents and python notebooks that rendered to HTML and published on `readthedocs `_ on each release. + + +Building the documentation +^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +The documentation can be built locally like so:: + + cd docs/ # enter the documentation folder + pip install -r requirements.txt # install documentation toolchain + make html + +After this, the documentation will be available as a set of HTML pages in the `_build/html` folder and can be visualized with a regular web browser. + + +Updating the Reference API +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The reference API part of the documentation is built automatically by the `docs/generate_api_doc.py `_ script. +The script contains the list of classes that will appear in the documentation. +If you contribute a new *user-facing* class, the list of classes should be updated to make it appear in the API reference. + + + + + +Issue Tracking +-------------- + +Issue tracking is done in GitHub issues: https://github.com/aiplan4eu/unified-planning/issues diff --git a/docs/index.rst b/docs/index.rst index a6740cf01..116dd1baa 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -32,7 +32,7 @@ Welcome to Unified-Planning documentation! optimality examples notebooks/engines/README - contributions - issue_tracking + contributor_guide release_notes + contributions api/index From c9eb524583e9f211c509b80bf296ff5c930af490 Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Wed, 8 Nov 2023 14:00:27 +0100 Subject: [PATCH 03/19] fix(doc): Fix syntax error in markdown source (CONTRIBUTING.md) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b1faf0804..507b1af39 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -110,7 +110,7 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/aiplan ### Code Contribution -Before you start coding, open an issue or a discussion on the project [GitHub space]((https://github.com/aiplan4eu/unified-planning) or get in contact with a maintainer (you can use the project mailing list: unified-planning@googlegroups.com)! It is possible that someone else is already working on the same or a related fix or feature! +Before you start coding, open an issue or a discussion on the project [GitHub space](https://github.com/aiplan4eu/unified-planning) or get in contact with a maintainer (you can use the project mailing list: unified-planning@googlegroups.com)! It is possible that someone else is already working on the same or a related fix or feature! Moreover, we have to keep a reasonable project scope, so if your contribution is wildly beyond the current capabilities of the library, it is better to discuss the idea with the community and the maintainers to avoid unpleasant situations where we have to reject your code. From 4dc202dcdae02293b66a257bc99abdafe66fe559 Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Wed, 8 Nov 2023 14:33:52 +0100 Subject: [PATCH 04/19] doc: Initial work on improved engine pages --- docs/.gitignore | 2 +- docs/conf.py | 61 ++++++------ docs/engines.rst | 69 +------------- docs/engines/01_available_engines.rst | 124 +++++++++++++++++++++++++ docs/engines/02_engine_selection.rst | 63 +++++++++++++ docs/engines/03_integrating_engine.rst | 7 ++ docs/engines/04_testing_engine.rst | 4 + 7 files changed, 236 insertions(+), 94 deletions(-) create mode 100644 docs/engines/01_available_engines.rst create mode 100644 docs/engines/02_engine_selection.rst create mode 100644 docs/engines/03_integrating_engine.rst create mode 100644 docs/engines/04_testing_engine.rst diff --git a/docs/.gitignore b/docs/.gitignore index 7964657d9..8b1378917 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1 +1 @@ -engines/* + diff --git a/docs/conf.py b/docs/conf.py index c4394dcac..a3867d83d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -336,34 +336,37 @@ autodoc_member_order = "bysource" -# -- Generating Planning Engine Scripts ------------------------------------------- - -engines = { - "aries": "https://raw.githubusercontent.com/plaans/aries/master/planning/unified/plugin/README.md", - "tamer": "https://raw.githubusercontent.com/aiplan4eu/up-tamer/master/README.md", - "enhsp": "https://raw.githubusercontent.com/aiplan4eu/up-enhsp/master/README.md", - "spiderplan": "https://raw.githubusercontent.com/aiplan4eu/up-spiderplan/master/README.md", - "fmap": "https://raw.githubusercontent.com/aiplan4eu/up-fmap/master/README.md", - "lpg": "https://raw.githubusercontent.com/aiplan4eu/up-lpg/master/README.md", - "pyperplan": "https://raw.githubusercontent.com/aiplan4eu/up-pyperplan/master/README.md", - "fast_downward": "https://raw.githubusercontent.com/aiplan4eu/up-fast-downward/main/README.md", -} - -SKIP_ENGINES = [] -for skipped in SKIP_ENGINES: - engines.popitem(skipped) -engines = dict(sorted(engines.items())) -ENGINES_DIR = os.path.join(os.path.dirname(__file__), "engines") - -if not os.path.exists(ENGINES_DIR): - os.makedirs(ENGINES_DIR) - -for i, (name, source) in enumerate(engines.items()): - with open(f"{ENGINES_DIR}/{i+1}-{name}.md", "w") as f: - response = requests.get(source) - if response.status_code == 200: - f.write(response.text) - else: - Warning(f"Error getting source for planning engine {name}") +# # -- Generating Planning Engine Scripts ------------------------------------------- + +# engines = { +# "aries": "https://raw.githubusercontent.com/plaans/aries/master/planning/unified/plugin/README.md", +# "tamer": "https://raw.githubusercontent.com/aiplan4eu/up-tamer/master/README.md", +# "enhsp": "https://raw.githubusercontent.com/aiplan4eu/up-enhsp/master/README.md", +# "spiderplan": "https://raw.githubusercontent.com/aiplan4eu/up-spiderplan/master/README.md", +# "fmap": "https://raw.githubusercontent.com/aiplan4eu/up-fmap/master/README.md", +# "lpg": "https://raw.githubusercontent.com/aiplan4eu/up-lpg/master/README.md", +# "pyperplan": "https://raw.githubusercontent.com/aiplan4eu/up-pyperplan/master/README.md", +# "fast_downward": "https://raw.githubusercontent.com/aiplan4eu/up-fast-downward/main/README.md", +# } + +# SKIP_ENGINES = [] +# for skipped in SKIP_ENGINES: +# engines.popitem(skipped) +# engines = dict(sorted(engines.items())) +# ENGINES_DIR = os.path.join(os.path.dirname(__file__), "engines") + +# if not os.path.exists(ENGINES_DIR): +# os.makedirs(ENGINES_DIR) + +# for i, (name, source) in enumerate(engines.items()): +# with open(f"{ENGINES_DIR}/{i+1}-{name}.md", "w") as f: +# response = requests.get(source) +# if response.status_code == 200: +# f.write(response.text) +# else: +# Warning(f"Error getting source for planning engine {name}") + + +# Create API reference doc generate_api_doc.generate() diff --git a/docs/engines.rst b/docs/engines.rst index c77aa40ce..d5e479cc6 100644 --- a/docs/engines.rst +++ b/docs/engines.rst @@ -3,69 +3,10 @@ Planning Engines The ``Engine`` class is the class interface that has to be implemented in order to define an engine that exposes one or more operative modes. It has some methods that implements that operation modes and other methods that can be called to know if the engine is suitable for a given problem kind. -Engine selection and preference list ------------------------------------- - -The UP library instantiates planning engines via the ``unified_planning.engines.Factory`` class. A single instance of ``Factory`` is needed for each environment and can be retrieved by the factory property of an ``Environment`` object. This class maintains a set of known engines each with a unique name, and offers methods to add new engines by specifying the name and the python class, to instantiate and retrieve planning engines by name and to read configuration files in custom locations. - -If operation modes are invoked without specifying the name of an engine to use, the UP library will filter the list of engines known to the ``Factory`` by checking which engine supports the given ``problem kind`` with the given operation mode. If more than one engine passes the check, the ``Factory`` uses a `customizable` preference list to select the engine to retrieve. The default preference list is a simple heuristic we developed, but a user can override it by setting the ``preference_list`` property of the ``Factory``. - -A user can set a custom preference list and add external engines to the library by creating a configuration file called either ``up.ini`` or ``.up.ini`` and located in any of the parent directories from which the program code was called. Alternatively also ``~/up.ini``, ``~/.up.ini``, ``~/.uprc`` are valid files that the library checks by default. The syntax of the configuration files is straightforward and depicted below. - -.. code-block:: - :caption: Preference List structure - - [global] - engine_preference_list: - - - [engine ] - module_name: - class_name: - -Plug-in system --------------- - -As mentioned at the beginning, one of key characteristics of the UP library is the engine plug-in system. The idea is that the set of planning engines is not fixed a-priori and can be easily extended. In this section we detail this mechanism together with the concept of “meta-engine”, which is an engine using another engine as a service to achieve a certain operation mode. An overview of the engines integrated throughout the project by the consortium partners is available in this section. - -Meta-Engines ------------- -In addition to plain planning engines, the UP library embeds the concept of “meta-engine”. A meta-engine is a planning engine that needs access to another engine (or meta-engine) services to provide its own services. For example, the ``NaiveReplanner`` meta-engine (partially reported in the snippet below) implements the ``Replanner`` OperationMode by repeatedly calling any ``OneshotPlanner`` engine internally. - -.. code-block:: - :caption: NaiveReplanner partial implementation and usage - - class NaiveReplanner(MetaEngine, mixins.ReplannerMixin): - ... - - def _resolve(self, timeout, output_stream): - return self.engine.solve(self._problem, timeout, output_stream) - - def _update_initial_value(self, fluent, value): - self._problem.set_initial_value(fluent, value) - - def _add_goal(self, goal): - self._problem.add_goal(goal) - - def _add_action(self, action): - self._problem.add_action(action) - - ... - - - factory.add_meta_engine("naive-replanner", __name__, "NaiveReplanner") - - - problem = … - with Replanner(name="naive-replanner[tamer]", problem=problem) as replanner: - result = replanner.resolve() - ... - -The general idea of a meta-engine is to implement algorithms relying on planning engines to implement certain operation modes. The library uses a special naming convention for meta engines; if the meta engine is called ``meta``, its instantiation with the engine ``e`` is called ``meta[e]``. One can add meta-engine to the library via the ``Factory`` class, similarly to engines, the ``add_meta_engine(name: str, module_name: str, class_name: str)`` method allows the user to add new meta engines, which are automatically instantiated with all the compatible engines. Compatibility between an engine and a meta-engine is determined by the UP thanks to the MetaEngine class that is the base of every meta-engine: each meta-engine must implement the ``is_compatible_engine(engine: Type[Engine])`` method, which checks if a given engine is compatible with the meta-engine at hand. This schema allows the creation of very general algorithms whose capability varies depending on the engine they are instantiated with. - .. toctree:: - :hidden: - :glob: - :titlesonly: + :maxdepth: 2 + :glob: + :hidden: + :caption: Getting Started - engines/* + engines/* \ No newline at end of file diff --git a/docs/engines/01_available_engines.rst b/docs/engines/01_available_engines.rst new file mode 100644 index 000000000..a96fac7cc --- /dev/null +++ b/docs/engines/01_available_engines.rst @@ -0,0 +1,124 @@ + +Available Engines +================= + + + +The tables below give a high-level overview of the planning engines integrated with the UP. + +Action-Based Planning +^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + + * - Engine + - Operation modes + - Classical + - Numeric + - Temporal + - Metrics + * - `Fast-Downward`_ + - OneShot, Anytime + - 🗸 + - + - + - plan length, action costs + * - `ENHSP`_ + - OneShot, Anytime + - 🗸 + - 🗸 + - + - action costs, final value + * - `Tamer`_ + - OneShot + - 🗸 + - 🗸 + - 🗸 + - + * - `Aries`_ [#aries-actions]_ + - OneShot, Anytime + - 🗸 + - 🗸 (integers) + - 🗸 + - plan length, makespan, action costs + * - `Pyperplan`_ [#pyperplan-note]_ + - OneShot + - 🗸 + - + - + - + +.. [#aries-actions] Aries' focus is on hierarchical planning and scheduling and is likely not competitive with other planners in action-based planning. +.. [#pyperplan-note] Pyperplan is mostly intended for education purposes and cannot be expected to scale to non-trivial problems. + + +Plan Validation +^^^^^^^^^^^^^^^ + +.. list-table:: + + * - Engine + - Action-Based planning + - Numeric + - Temporal + - Hierarchical + - Scheduling + * - `UP (builtin)` + - 🗸 + - + - + - + - + * - `Tamer`_ + - 🗸 + - 🗸 + - 🗸 + - + - + * - `Aries`_ + - 🗸 + - 🗸 + - 🗸 + - 🗸 + - 🗸 + + +Hierarchical Planning +^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + + * - Engine + - Operation modes + - Total-Order + - Partial-Order + - Numeric + - Temporal + - Metrics + * - `Aries`_ + - OneShot, Anytime + - 🗸 + - 🗸 (integers) + - 🗸 + - 🗸 + - plan length, makespan, action costs + +A WIP integration is known for `SIADEX `_. + +Scheduling +^^^^^^^^^^ + +The only planner with full support for scheduling is `Aries`_. Integration work is known for the `discrete-optimization suite `_ and for `PPS `_. + + + + + +.. _`aries`: https://github.com/plaans/aries/blob/master/planning/unified/plugin/README.md +.. _`fast-downward`: https://github.com/aiplan4eu/up-fast-downward/blob/main/README.md +.. _`tamer`: https://github.com/aiplan4eu/up-tamer/blob/master/README.md +.. _`enhsp`: https://github.com/aiplan4eu/up-enhsp/blob/master/README.md +.. _`spiderplan`: https://github.com/aiplan4eu/up-spiderplan/blob/master/README.md +.. _`fmap`: https://github.com/aiplan4eu/up-fmap/blob/master/README.md +.. _`lpg`: https://github.com/aiplan4eu/up-lpg/blob/master/README.md +.. _`pyperplan`: https://github.com/aiplan4eu/up-pyperplan/blob/master/README.md \ No newline at end of file diff --git a/docs/engines/02_engine_selection.rst b/docs/engines/02_engine_selection.rst new file mode 100644 index 000000000..e7d4e4933 --- /dev/null +++ b/docs/engines/02_engine_selection.rst @@ -0,0 +1,63 @@ + +Engine selection and preference list +==================================== + +The UP library instantiates planning engines via the ``unified_planning.engines.Factory`` class. A single instance of ``Factory`` is needed for each environment and can be retrieved by the factory property of an ``Environment`` object. This class maintains a set of known engines each with a unique name, and offers methods to add new engines by specifying the name and the python class, to instantiate and retrieve planning engines by name and to read configuration files in custom locations. + +If operation modes are invoked without specifying the name of an engine to use, the UP library will filter the list of engines known to the ``Factory`` by checking which engine supports the given ``problem kind`` with the given operation mode. If more than one engine passes the check, the ``Factory`` uses a `customizable` preference list to select the engine to retrieve. The default preference list is a simple heuristic we developed, but a user can override it by setting the ``preference_list`` property of the ``Factory``. + +A user can set a custom preference list and add external engines to the library by creating a configuration file called either ``up.ini`` or ``.up.ini`` and located in any of the parent directories from which the program code was called. Alternatively also ``~/up.ini``, ``~/.up.ini``, ``~/.uprc`` are valid files that the library checks by default. The syntax of the configuration files is straightforward and depicted below. + +.. code-block:: + :caption: Preference List structure + + [global] + engine_preference_list: + + + [engine ] + module_name: + class_name: + +Plug-in system +-------------- + +As mentioned at the beginning, one of key characteristics of the UP library is the engine plug-in system. The idea is that the set of planning engines is not fixed a-priori and can be easily extended. In this section we detail this mechanism together with the concept of “meta-engine”, which is an engine using another engine as a service to achieve a certain operation mode. An overview of the engines integrated throughout the project by the consortium partners is available in this section. + +Meta-Engines +------------ +In addition to plain planning engines, the UP library embeds the concept of “meta-engine”. A meta-engine is a planning engine that needs access to another engine (or meta-engine) services to provide its own services. For example, the ``NaiveReplanner`` meta-engine (partially reported in the snippet below) implements the ``Replanner`` OperationMode by repeatedly calling any ``OneshotPlanner`` engine internally. + +.. code-block:: + :caption: NaiveReplanner partial implementation and usage + + class NaiveReplanner(MetaEngine, mixins.ReplannerMixin): + ... + + def _resolve(self, timeout, output_stream): + return self.engine.solve(self._problem, timeout, output_stream) + + def _update_initial_value(self, fluent, value): + self._problem.set_initial_value(fluent, value) + + def _add_goal(self, goal): + self._problem.add_goal(goal) + + def _add_action(self, action): + self._problem.add_action(action) + + ... + + + factory.add_meta_engine("naive-replanner", __name__, "NaiveReplanner") + + + problem = … + with Replanner(name="naive-replanner[tamer]", problem=problem) as replanner: + result = replanner.resolve() + ... + +The general idea of a meta-engine is to implement algorithms relying on planning engines to implement certain operation modes. The library uses a special naming convention for meta engines; if the meta engine is called ``meta``, its instantiation with the engine ``e`` is called ``meta[e]``. One can add meta-engine to the library via the ``Factory`` class, similarly to engines, the ``add_meta_engine(name: str, module_name: str, class_name: str)`` method allows the user to add new meta engines, which are automatically instantiated with all the compatible engines. Compatibility between an engine and a meta-engine is determined by the UP thanks to the MetaEngine class that is the base of every meta-engine: each meta-engine must implement the ``is_compatible_engine(engine: Type[Engine])`` method, which checks if a given engine is compatible with the meta-engine at hand. This schema allows the creation of very general algorithms whose capability varies depending on the engine they are instantiated with. + + + diff --git a/docs/engines/03_integrating_engine.rst b/docs/engines/03_integrating_engine.rst new file mode 100644 index 000000000..69e4f62e1 --- /dev/null +++ b/docs/engines/03_integrating_engine.rst @@ -0,0 +1,7 @@ +Integrating an Engine +===================== + +TODO: populate with ICAPS tutorial notebook + + + diff --git a/docs/engines/04_testing_engine.rst b/docs/engines/04_testing_engine.rst new file mode 100644 index 000000000..5b6cbe748 --- /dev/null +++ b/docs/engines/04_testing_engine.rst @@ -0,0 +1,4 @@ +Testing an Engine Integration +============================= + +TODO: how to run report, ... (maybe merge with "integrating") \ No newline at end of file From 906c9127561490c63f695dca8ce346746a0046b9 Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Thu, 9 Nov 2023 11:04:35 +0100 Subject: [PATCH 05/19] doc: improvements to scheduling notebook. --- docs/notebooks/13-scheduling.ipynb | 110 +++++++++++++++++++++++------ 1 file changed, 87 insertions(+), 23 deletions(-) diff --git a/docs/notebooks/13-scheduling.ipynb b/docs/notebooks/13-scheduling.ipynb index 68b77b42c..9177a7b3e 100644 --- a/docs/notebooks/13-scheduling.ipynb +++ b/docs/notebooks/13-scheduling.ipynb @@ -9,7 +9,7 @@ } }, "source": [ - "# Hierarchical Planning\n", + "# Scheduling\n", "\n" ] }, @@ -43,7 +43,7 @@ }, "outputs": [], "source": [ - "%pip install unified-planning" + "%pip install unified-planning[aries]" ] }, { @@ -54,13 +54,13 @@ } }, "source": [ - "# A *scheduling* primer\n", + "## A *scheduling* primer\n", "\n", - "`unified-planning` provides initial support for modeling scheduling problems:\n", + "`unified-planning` provides support for modeling *scheduling problems*. It heavily relies on all the building blocks of planning problems and in particular:\n", "\n", - " - reuse state definition (`Type`,`Fluent`, initial state, (timed) goals, ...)\n", + " - reuse state definition (`Type`, `Fluent`, initial state, (timed) goals, ...)\n", " - replace `Action` with `Activity`\n", - " - add syntactic sugar for common patterns in scheduling problems\n", + " - add syntactic sugar for common patterns in scheduling problems (resources, ...)\n", " \n", " \n", " \n", @@ -69,7 +69,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 13, "metadata": { "scrolled": true, "slideshow": { @@ -99,7 +99,7 @@ " " ] }, - "execution_count": 2, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -130,7 +130,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 14, "metadata": { "slideshow": { "slide_type": "-" @@ -187,12 +187,12 @@ "source": [ "## Activities\n", "\n", - "An [`Activity`](https://unified-planning.readthedocs.io/en/latest/api/model/scheduling/Activity.html) is essentially a **durative action** present **exactly once** in the solution." + "An [`Activity`](https://unified-planning.readthedocs.io/en/latest/api/model/scheduling/Activity.html) is essentially a **durative action** that appears **exactly once** in the solution." ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 15, "metadata": { "slideshow": { "slide_type": "-" @@ -225,7 +225,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 16, "metadata": { "slideshow": { "slide_type": "fragment" @@ -258,7 +258,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 17, "metadata": { "slideshow": { "slide_type": "slide" @@ -284,7 +284,7 @@ " }" ] }, - "execution_count": 6, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -302,7 +302,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 23, "metadata": { "scrolled": true, "slideshow": { @@ -328,8 +328,14 @@ "]\n", "\n", "initial values = [\n", + " machine1 := 1\n", + " machine2 := 1\n", + " operators := 4\n", "]\n", "\n", + "quality metrics = [\n", + " minimize makespan\n", + "]\n", "\n", "BASE: {\n", " constraints = [\n", @@ -338,8 +344,10 @@ " effects = [\n", " start + 17:\n", " operators -= 1:\n", + " operators -= 1:\n", " start + 25:\n", " operators += 1:\n", + " operators += 1:\n", " ]\n", " }\n", "\n", @@ -372,13 +380,13 @@ " " ] }, - "execution_count": 7, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# finish a2 before starting a1\n", + "# finish a2 strictly before starting a1\n", "problem.add_constraint(LT(a2.end, a1.start))\n", "\n", "# One worker is unavailable over [17, 25)\n", @@ -387,6 +395,22 @@ "problem" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As a last step, lets just specify that we want the makespan to be minimized." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "problem.add_quality_metric(MinimizeMakespan())" + ] + }, { "cell_type": "markdown", "metadata": { @@ -402,7 +426,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -412,9 +436,11 @@ "PROBLEM_CLASS: ['SCHEDULING']\n", "PROBLEM_TYPE: ['SIMPLE_NUMERIC_PLANNING']\n", "TIME: ['TIMED_EFFECTS', 'DISCRETE_TIME']\n", - "NUMBERS: ['DISCRETE_NUMBERS', 'BOUNDED_TYPES']\n", + "EXPRESSION_DURATION: ['INT_TYPE_DURATIONS']\n", + "NUMBERS: ['BOUNDED_TYPES']\n", "EFFECTS_KIND: ['DECREASE_EFFECTS', 'INCREASE_EFFECTS']\n", - "FLUENTS_TYPE: ['NUMERIC_FLUENTS']\n" + "FLUENTS_TYPE: ['INT_FLUENTS']\n", + "QUALITY_METRICS: ['MAKESPAN']\n" ] } ], @@ -430,10 +456,48 @@ } }, "source": [ - " Currently no solver are provided in the UP that support scheduling problems natively.\n", - " \n", - " However, early support is provided in the development versions of the [aries](https://github.com/plaans/aries/tree/master/planning/unified/plugin) and [discrete-optimization](https://github.com/aiplan4eu/up-discreteoptimization) backends.\n", + " Currently, the only solver supporting scheduling problems is [aries](https://github.com/plaans/aries/tree/master/planning/unified/plugin).\n", + " When asking for a oneshot solver, it would automatically be selected to solve the problem.\n", "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "status: SOLVED_OPTIMALLY\n", + "engine: aries\n", + "plan: Schedule:\n", + " [0, 6] a2\n", + " [7, 10] a1\n", + "\n" + ] + } + ], + "source": [ + "with OneshotPlanner(problem_kind=problem.kind) as planner:\n", + " res = planner.solve(problem)\n", + " print(res)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, all activities are present in the solution, with `a2` finishing strictly before starting `a1` as imposed in the problem's constraints." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Going Further\n", "\n", "Reference: [Complete parser for jobshop (with operators) problems](https://github.com/aiplan4eu/unified-planning/blob/master/unified_planning/test/examples/scheduling/jobshop.py)" ] From 427c6b7d4454fb1b3f4915313f5e2138e87f93d4 Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Thu, 9 Nov 2023 13:13:07 +0100 Subject: [PATCH 06/19] doc: Import ICAPS modeling tutorial into getting started section. Co-authored-by: Gabi Roeger --- docs/getting_started.rst | 1 + docs/notebooks/tutorial/modeling.ipynb | 1715 ++++++++++++++++++++++++ 2 files changed, 1716 insertions(+) create mode 100644 docs/notebooks/tutorial/modeling.ipynb diff --git a/docs/getting_started.rst b/docs/getting_started.rst index bacbe5859..eadd8991d 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -16,3 +16,4 @@ In this guide we present the main functionalities offered by the Unified-Plannin getting_started/installation getting_started/quickstart + notebooks/tutorial/modeling.ipynb diff --git a/docs/notebooks/tutorial/modeling.ipynb b/docs/notebooks/tutorial/modeling.ipynb new file mode 100644 index 000000000..ba699d7ed --- /dev/null +++ b/docs/notebooks/tutorial/modeling.ipynb @@ -0,0 +1,1715 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "ZHx6ZJ5OHimO", + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Modelling and Solving Planning Tasks" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cRsOFdHXLozf", + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Let's first install the unified planning library." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "a7kQWSWSHgXl", + "outputId": "62058f78-3dfc-4be5-ed71-ea97b9ffad27", + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Defaulting to user installation because normal site-packages is not writeable\n", + "Requirement already satisfied: unified-planning[engines] in /home/roeger/.local/lib/python3.10/site-packages (1.0.0)\n", + "Requirement already satisfied: pyparsing in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (3.1.0a1)\n", + "Requirement already satisfied: networkx in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (2.8)\n", + "Requirement already satisfied: up-fast-downward==0.2.3 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (0.2.3)\n", + "Requirement already satisfied: up-lpg==0.0.7 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (0.0.7)\n", + "Requirement already satisfied: up-aries>=0.0.8 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (0.1.0)\n", + "Requirement already satisfied: up-enhsp==0.0.15 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (0.0.15)\n", + "Requirement already satisfied: tarski[arithmetic] in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (0.8.2)\n", + "Requirement already satisfied: up-tamer==1.0.0 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (1.0.0)\n", + "Requirement already satisfied: up-pyperplan==1.0.0 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (1.0.0)\n", + "Requirement already satisfied: up-symk>=0.0.3 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (0.0.4)\n", + "Requirement already satisfied: up-fmap==0.0.7 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (0.0.7)\n", + "Requirement already satisfied: pyperplan==2.1 in /home/roeger/.local/lib/python3.10/site-packages (from up-pyperplan==1.0.0->unified-planning[engines]) (2.1)\n", + "Requirement already satisfied: pytamer==0.1.15 in /home/roeger/.local/lib/python3.10/site-packages (from up-tamer==1.0.0->unified-planning[engines]) (0.1.15)\n", + "Requirement already satisfied: wheel in /usr/lib/python3/dist-packages (from pyperplan==2.1->up-pyperplan==1.0.0->unified-planning[engines]) (0.37.1)\n", + "Requirement already satisfied: grpcio in /home/roeger/.local/lib/python3.10/site-packages (from up-aries>=0.0.8->unified-planning[engines]) (1.56.0)\n", + "Requirement already satisfied: pytest in /home/roeger/.local/lib/python3.10/site-packages (from up-aries>=0.0.8->unified-planning[engines]) (7.4.0)\n", + "Requirement already satisfied: grpcio-tools in /home/roeger/.local/lib/python3.10/site-packages (from up-aries>=0.0.8->unified-planning[engines]) (1.56.0)\n", + "Requirement already satisfied: psutil in /home/roeger/.local/lib/python3.10/site-packages (from tarski[arithmetic]->unified-planning[engines]) (5.9.5)\n", + "Requirement already satisfied: antlr4-python3-runtime==4.7.2 in /home/roeger/.local/lib/python3.10/site-packages (from tarski[arithmetic]->unified-planning[engines]) (4.7.2)\n", + "Requirement already satisfied: multipledispatch in /home/roeger/.local/lib/python3.10/site-packages (from tarski[arithmetic]->unified-planning[engines]) (1.0.0)\n", + "Requirement already satisfied: numpy in /usr/lib/python3/dist-packages (from tarski[arithmetic]->unified-planning[engines]) (1.21.5)\n", + "Requirement already satisfied: scipy in /usr/lib/python3/dist-packages (from tarski[arithmetic]->unified-planning[engines]) (1.8.0)\n", + "Requirement already satisfied: protobuf<5.0dev,>=4.21.6 in /home/roeger/.local/lib/python3.10/site-packages (from grpcio-tools->up-aries>=0.0.8->unified-planning[engines]) (4.23.3)\n", + "Requirement already satisfied: setuptools in /usr/lib/python3/dist-packages (from grpcio-tools->up-aries>=0.0.8->unified-planning[engines]) (59.6.0)\n", + "Requirement already satisfied: packaging in /usr/lib/python3/dist-packages (from pytest->up-aries>=0.0.8->unified-planning[engines]) (21.3)\n", + "Requirement already satisfied: pluggy<2.0,>=0.12 in /usr/lib/python3/dist-packages (from pytest->up-aries>=0.0.8->unified-planning[engines]) (0.13.0)\n", + "Requirement already satisfied: tomli>=1.0.0 in /home/roeger/.local/lib/python3.10/site-packages (from pytest->up-aries>=0.0.8->unified-planning[engines]) (2.0.1)\n", + "Requirement already satisfied: iniconfig in /home/roeger/.local/lib/python3.10/site-packages (from pytest->up-aries>=0.0.8->unified-planning[engines]) (2.0.0)\n", + "Requirement already satisfied: exceptiongroup>=1.0.0rc8 in /home/roeger/.local/lib/python3.10/site-packages (from pytest->up-aries>=0.0.8->unified-planning[engines]) (1.1.0)\n" + ] + } + ], + "source": [ + "!pip install unified-planning[engines]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9vkgA7cFH1_W", + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "* Classes to represent planning problems are in the [`unified_planning.model`](https://unified-planning.readthedocs.io/en/latest/api/model/index.html) package.\n", + "* We also use the [`unified_planning.shortcuts`](https://github.com/aiplan4eu/unified-planning/blob/master/unified_planning/shortcuts.py) package for simplified access to the most common features.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "id": "aX_XEsUcKzIP", + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "from unified_planning.shortcuts import * " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gprEusO3QcsQ", + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Modelling States" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "D83UoWMKQkna", + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Fluents\n", + "\n", + "* Fluents define the language for specifying states\n", + "* a `Fluent` is specified by a\n", + " * `name` – a string\n", + " * `type` – a fluent type\n", + " * `signature` – list of parameters (name + type with finite domain)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XAm_a_78R2rb", + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "* Available types relevant for classical and numerical problems\n", + " * `BoolType()`\n", + " * `UserType(name, father=None)`\n", + " * `IntType(lower_bound=None, upper_bound=None)`\n", + " * `RealType(lower_bound=None, upper_bound=None)`\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "XZtPHmYnQ4Ob", + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "location = UserType(\"Location\")\n", + "person = UserType(\"Person\")\n", + "conference_attendee = UserType(\"Attendee\", father=person)\n", + "laptop = UserType(\"Laptop\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "id": "nbxlmbFxQ14f", + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "arrived = Fluent(\"arrived\", BoolType(), person=conference_attendee)\n", + "destination = Fluent(\"destination\", location, person=conference_attendee)\n", + "distance = Fluent(\"distance\", IntType(0, 100), l_from=location, l_to=location)\n", + "battery_level = Fluent(\"battery_level\", RealType(0.0, 1.0), l=laptop)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kyO8atTrorsX", + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Objects\n", + "\n", + "Declare an [`Object`](https://unified-planning.readthedocs.io/en/latest/api/model/Object.html) with `Object(name, typename, environment=None)`." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "sPbtqMo_Qf-k", + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "basel = Object(\"Basel\", location)\n", + "prague = Object(\"Prague\", location)\n", + "gabi = Object(\"Gabi\", conference_attendee)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "We can use objects to instantiate fluents:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "id": "yXOUGV0cqKUc", + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "dest_gabi = destination(gabi)\n", + "arrived_gabi = arrived(gabi)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "unified_planning.model.fnode.FNode" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "arrived_gabi.__class__" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "bool arrived[person=Attendee - Person]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "arrived_gabi.fluent()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(Gabi,)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "arrived_gabi.args" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Creating a Problem" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "For classical, numeric and temporal planning, we use class [`Problem`](https://unified-planning.readthedocs.io/en/latest/api/model/Problem.html). " + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "problem = Problem(\"Travel to ICAPS\")\n", + "problem.add_fluent(destination, default_initial_value=prague)\n", + "problem.add_objects([gabi, basel, prague])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "We don't have to create fluents separately, but can also do so while adding them:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "fluent_at = problem.add_fluent(\"at\", location, p=person)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Location at[p=Person]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# You can also get hold of a fluent later:\n", + "problem.fluent(\"at\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Analogously for objects:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "problem.add_object(\"Mum\", person)\n", + "problem.add_objects(Object(name, conference_attendee) for name in (\"Andrea\", \"Arthur\", \"Sebastian\"))\n", + "problem.add_objects(Object(name, location) for name in (\"Trento\", \"Toulouse\", \"Bremen\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[Gabi, Andrea, Arthur, Sebastian]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(problem.objects(conference_attendee))" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Prague \n" + ] + } + ], + "source": [ + "p = problem.object(\"Prague\")\n", + "print(p, p.__class__)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "How does the problem look like so far?" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "problem name = Travel to ICAPS\n", + "\n", + "types = [Location, Person, Attendee - Person]\n", + "\n", + "fluents = [\n", + " Location destination[person=Attendee - Person]\n", + " Location at[p=Person]\n", + "]\n", + "\n", + "actions = [\n", + "]\n", + "\n", + "objects = [\n", + " Location: [Basel, Prague, Trento, Toulouse, Bremen]\n", + " Person: [Gabi, Mum, Andrea, Arthur, Sebastian]\n", + " Attendee - Person: [Gabi, Andrea, Arthur, Sebastian]\n", + "]\n", + "\n", + "initial fluents default = [\n", + " Location destination[person=Attendee - Person] := Prague\n", + "]\n", + "\n", + "initial values = [\n", + "]\n", + "\n", + "goals = [\n", + "]\n" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "problem" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Let's set the (rest of the) initial state:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "initially_at = [(\"Andrea\", \"Trento\"), (\"Arthur\", \"Toulouse\"), (\"Sebastian\", \"Bremen\"),\n", + " (\"Gabi\", \"Basel\"), (\"Mum\", \"Basel\")]\n", + "for name, loc in initially_at:\n", + " problem.set_initial_value(fluent_at(problem.object(name)), problem.object(loc))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "problem name = Travel to ICAPS\n", + "\n", + "types = [Location, Person, Attendee - Person]\n", + "\n", + "fluents = [\n", + " Location destination[person=Attendee - Person]\n", + " Location at[p=Person]\n", + "]\n", + "\n", + "actions = [\n", + "]\n", + "\n", + "objects = [\n", + " Location: [Basel, Prague, Trento, Toulouse, Bremen]\n", + " Person: [Gabi, Mum, Andrea, Arthur, Sebastian]\n", + " Attendee - Person: [Gabi, Andrea, Arthur, Sebastian]\n", + "]\n", + "\n", + "initial fluents default = [\n", + " Location destination[person=Attendee - Person] := Prague\n", + "]\n", + "\n", + "initial values = [\n", + " at(Andrea) := Trento\n", + " at(Arthur) := Toulouse\n", + " at(Sebastian) := Bremen\n", + " at(Gabi) := Basel\n", + " at(Mum) := Basel\n", + "]\n", + "\n", + "goals = [\n", + "]\n" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "problem" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Goal\n", + "\n", + "We need a goal. All attendees should be at their destination." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[Forall (Attendee - Person a) (at(a) == destination(a))]" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = Variable(\"a\", conference_attendee)\n", + "problem.add_goal(Forall(fluent_at(a).Equals(destination(a)), a))\n", + "\n", + "problem.goals" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Actions\n", + "\n", + "Let's first add a very simple action to let persons travel between locations..." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[action travel(Person p, Location l_from, Location l_to) {\n", + " preconditions = [\n", + " (at(p) == l_from)\n", + " ]\n", + " effects = [\n", + " at(p) := l_to\n", + " ]\n", + " }]" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "travel = InstantaneousAction('travel', p=person, l_from=location, l_to=location)\n", + "p = travel.p\n", + "l_from = travel.l_from\n", + "l_to = travel.l_to\n", + "travel.add_precondition(fluent_at(p).Equals(l_from))\n", + "travel.add_effect(fluent_at(p), l_to)\n", + "problem.add_action(travel)\n", + "problem.actions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Action Costs & Metrics\n", + "\n", + "We also want to consider the travel times between the different locations and optimize the accumulated travel time of everyone getting to Prague.\n", + "\n", + "To represent the action costs, we use an integer fluent and set random travel times (for the sake of simplicity).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "import itertools\n", + "from random import randint\n", + "\n", + "travel_cost = Fluent(\"travel_cost\", IntType(), from_loc=location, to_loc=location)\n", + "problem.add_fluent(travel_cost, default_initial_value=Int(0))\n", + "\n", + "for loc1, loc2 in itertools.combinations([\"Prague\", \"Trento\", \"Toulouse\", \"Bremen\", \"Basel\"], 2):\n", + " dist = randint(1,100)\n", + " l1 = problem.object(loc1)\n", + " l2 = problem.object(loc2)\n", + " problem.set_initial_value(travel_cost(l1, l2), Int(dist))\n", + " problem.set_initial_value(travel_cost(l2, l1), Int(dist))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Now we can specify the metric as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "m = MinimizeActionCosts({travel : travel_cost(travel.l_from, travel.l_to)}, default=Int(1))\n", + "problem.clear_quality_metrics()\n", + "problem.add_quality_metric(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Problem Kind\n", + "\n", + "* We wildly added everything without considering what fragment of planning we are targeting.\n", + "* The library analyses what features we used by means of the [`ProblemKind`](https://unified-planning.readthedocs.io/en/latest/problem_representation.html#problem-kinds).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "ProblemKind(['UNIVERSAL_CONDITIONS', 'EQUALITIES', 'ACTIONS_COST', 'ACTION_BASED', 'HIERARCHICAL_TYPING', 'FLAT_TYPING', 'STATIC_FLUENTS_IN_ACTIONS_COST', 'OBJECT_FLUENTS'])" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "problem.kind" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "It's time to solve this incredibly hard problem..." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "But here at ICAPS we know that object fluents are not widely supported by planning engines... And indeed:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [ + "raises-exception" + ] + }, + "outputs": [ + { + "ename": "UPNoSuitableEngineAvailableException", + "evalue": "No available engine supports all the problem features:\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| Engine | UNIVERSAL_CONDITIONS | EQUALITIES | ACTIONS_COST | ACTION_BASED | HIERARCHICAL_TYPING | FLAT_TYPING | STATIC_FLUENTS_IN_ACTIONS_COST | OBJECT_FLUENTS |\n===============================================================================================================================================================================================\n| fast-downward | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| fast-downward-opt | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| symk | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| symk-opt | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| pyperplan | False | False | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| pyperplan-opt | False | False | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp-opt | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp-any | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| tamer | False | True | False | True | False | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg | False | True | False | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg-anytime | False | True | False | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg-repairer | False | True | False | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| fmap | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| aries | False | True | True | True | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[fast-downward] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[fast-downward-opt] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[symk] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[symk-opt] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp-opt] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp-any] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[tamer] | False | True | False | True | False | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[aries] | False | True | False | True | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mUPNoSuitableEngineAvailableException\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/tmp/ipykernel_133973/3821768881.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mwith\u001b[0m \u001b[0mOneshotPlanner\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mproblem_kind\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mproblem\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkind\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mplanner\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mres\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mplanner\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msolve\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mproblem\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mres\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.10/site-packages/unified_planning/shortcuts.py\u001b[0m in \u001b[0;36mOneshotPlanner\u001b[0;34m(name, names, params, problem_kind, optimality_guarantee)\u001b[0m\n\u001b[1;32m 547\u001b[0m \u001b[0;34m|\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0mOneshotPlanner\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mproblem_kind\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mproblem\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkind\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moptimality_guarantee\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mSOLVED_OPTIMALLY\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 548\u001b[0m \"\"\"\n\u001b[0;32m--> 549\u001b[0;31m return get_environment().factory.OneshotPlanner(\n\u001b[0m\u001b[1;32m 550\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 551\u001b[0m \u001b[0mnames\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnames\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.10/site-packages/unified_planning/engines/factory.py\u001b[0m in \u001b[0;36mOneshotPlanner\u001b[0;34m(self, name, names, params, problem_kind, optimality_guarantee)\u001b[0m\n\u001b[1;32m 744\u001b[0m \u001b[0;34mf\"{optimality_guarantee} is not a valid OptimalityGuarantee.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 745\u001b[0m )\n\u001b[0;32m--> 746\u001b[0;31m return self._get_engine(\n\u001b[0m\u001b[1;32m 747\u001b[0m \u001b[0mOperationMode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mONESHOT_PLANNER\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 748\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.10/site-packages/unified_planning/engines/factory.py\u001b[0m in \u001b[0;36m_get_engine\u001b[0;34m(self, operation_mode, name, names, params, problem_kind, optimality_guarantee, compilation_kind, compilation_kinds, plan_kind, anytime_guarantee, problem)\u001b[0m\n\u001b[1;32m 654\u001b[0m \u001b[0mparams\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 655\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mDict\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 656\u001b[0;31m EngineClass = self._get_engine_class(\n\u001b[0m\u001b[1;32m 657\u001b[0m \u001b[0moperation_mode\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 658\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.10/site-packages/unified_planning/engines/factory.py\u001b[0m in \u001b[0;36m_get_engine_class\u001b[0;34m(self, operation_mode, name, problem_kind, optimality_guarantee, compilation_kind, plan_kind, anytime_guarantee)\u001b[0m\n\u001b[1;32m 541\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 542\u001b[0m \u001b[0mmsg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34mf\"No available {operation_mode} engine\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 543\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mup\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexceptions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mUPNoSuitableEngineAvailableException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 544\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 545\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_print_credits\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mall_credits\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mSequence\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mOptional\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"up.engines.Credits\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mUPNoSuitableEngineAvailableException\u001b[0m: No available engine supports all the problem features:\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| Engine | UNIVERSAL_CONDITIONS | EQUALITIES | ACTIONS_COST | ACTION_BASED | HIERARCHICAL_TYPING | FLAT_TYPING | STATIC_FLUENTS_IN_ACTIONS_COST | OBJECT_FLUENTS |\n===============================================================================================================================================================================================\n| fast-downward | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| fast-downward-opt | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| symk | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| symk-opt | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| pyperplan | False | False | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| pyperplan-opt | False | False | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp-opt | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp-any | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| tamer | False | True | False | True | False | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg | False | True | False | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg-anytime | False | True | False | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg-repairer | False | True | False | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| fmap | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| aries | False | True | True | True | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[fast-downward] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[fast-downward-opt] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[symk] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[symk-opt] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp-opt] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp-any] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[tamer] | False | True | False | True | False | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[aries] | False | True | False | True | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------" + ] + } + ], + "source": [ + "with OneshotPlanner(problem_kind=problem.kind) as planner:\n", + " res = planner.solve(problem)\n", + " print(res) " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Using a Compiler\n", + "\n", + "For Fast Downward, the only unsupported feature were the object fluents.\n", + "\n", + "Let's use a compiler to eliminate them:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "with Compiler(\n", + " problem_kind = problem.kind,\n", + " compilation_kind = CompilationKind.USERTYPE_FLUENTS_REMOVING\n", + " ) as usertype_remover:\n", + " utr_result = usertype_remover.compile(\n", + " problem,\n", + " CompilationKind.USERTYPE_FLUENTS_REMOVING\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "problem name = utfr_Travel to ICAPS\n", + "\n", + "types = [Person, Attendee - Person, Location]\n", + "\n", + "fluents = [\n", + " bool destination[person=Attendee - Person, location=Location]\n", + " bool at[p=Person, location=Location]\n", + " integer travel_cost[from_loc=Location, to_loc=Location]\n", + "]\n", + "\n", + "actions = [\n", + " action travel(Person p, Location l_from, Location l_to) {\n", + " preconditions = [\n", + " at(p, l_from)\n", + " ]\n", + " effects = [\n", + " if (l_to == Basel) then at(p, Basel) := true\n", + " if (not (l_to == Basel)) then at(p, Basel) := false\n", + " if (l_to == Prague) then at(p, Prague) := true\n", + " if (not (l_to == Prague)) then at(p, Prague) := false\n", + " if (l_to == Trento) then at(p, Trento) := true\n", + " if (not (l_to == Trento)) then at(p, Trento) := false\n", + " if (l_to == Toulouse) then at(p, Toulouse) := true\n", + " if (not (l_to == Toulouse)) then at(p, Toulouse) := false\n", + " if (l_to == Bremen) then at(p, Bremen) := true\n", + " if (not (l_to == Bremen)) then at(p, Bremen) := false\n", + " ]\n", + " }\n", + "]\n", + "\n", + "objects = [\n", + " Person: [Gabi, Mum, Andrea, Arthur, Sebastian]\n", + " Attendee - Person: [Gabi, Andrea, Arthur, Sebastian]\n", + " Location: [Basel, Prague, Trento, Toulouse, Bremen]\n", + "]\n", + "\n", + "initial fluents default = [\n", + "]\n", + "\n", + "initial values = [\n", + " at(Andrea, Basel) := false\n", + " at(Andrea, Prague) := false\n", + " at(Andrea, Trento) := true\n", + " at(Andrea, Toulouse) := false\n", + " at(Andrea, Bremen) := false\n", + " at(Arthur, Basel) := false\n", + " at(Arthur, Prague) := false\n", + " at(Arthur, Trento) := false\n", + " at(Arthur, Toulouse) := true\n", + " at(Arthur, Bremen) := false\n", + " at(Sebastian, Basel) := false\n", + " at(Sebastian, Prague) := false\n", + " at(Sebastian, Trento) := false\n", + " at(Sebastian, Toulouse) := false\n", + " at(Sebastian, Bremen) := true\n", + " at(Gabi, Basel) := true\n", + " at(Gabi, Prague) := false\n", + " at(Gabi, Trento) := false\n", + " at(Gabi, Toulouse) := false\n", + " at(Gabi, Bremen) := false\n", + " at(Mum, Basel) := true\n", + " at(Mum, Prague) := false\n", + " at(Mum, Trento) := false\n", + " at(Mum, Toulouse) := false\n", + " at(Mum, Bremen) := false\n", + " travel_cost(Prague, Trento) := 49\n", + " travel_cost(Trento, Prague) := 49\n", + " travel_cost(Prague, Toulouse) := 53\n", + " travel_cost(Toulouse, Prague) := 53\n", + " travel_cost(Prague, Bremen) := 20\n", + " travel_cost(Bremen, Prague) := 20\n", + " travel_cost(Prague, Basel) := 78\n", + " travel_cost(Basel, Prague) := 78\n", + " travel_cost(Trento, Toulouse) := 89\n", + " travel_cost(Toulouse, Trento) := 89\n", + " travel_cost(Trento, Bremen) := 13\n", + " travel_cost(Bremen, Trento) := 13\n", + " travel_cost(Trento, Basel) := 9\n", + " travel_cost(Basel, Trento) := 9\n", + " travel_cost(Toulouse, Bremen) := 58\n", + " travel_cost(Bremen, Toulouse) := 58\n", + " travel_cost(Toulouse, Basel) := 8\n", + " travel_cost(Basel, Toulouse) := 8\n", + " travel_cost(Bremen, Basel) := 44\n", + " travel_cost(Basel, Bremen) := 44\n", + " destination(Gabi, Basel) := false\n", + " destination(Gabi, Prague) := true\n", + " destination(Gabi, Trento) := false\n", + " destination(Gabi, Toulouse) := false\n", + " destination(Gabi, Bremen) := false\n", + " destination(Andrea, Basel) := false\n", + " destination(Andrea, Prague) := true\n", + " destination(Andrea, Trento) := false\n", + " destination(Andrea, Toulouse) := false\n", + " destination(Andrea, Bremen) := false\n", + " destination(Arthur, Basel) := false\n", + " destination(Arthur, Prague) := true\n", + " destination(Arthur, Trento) := false\n", + " destination(Arthur, Toulouse) := false\n", + " destination(Arthur, Bremen) := false\n", + " destination(Sebastian, Basel) := false\n", + " destination(Sebastian, Prague) := true\n", + " destination(Sebastian, Trento) := false\n", + " destination(Sebastian, Toulouse) := false\n", + " destination(Sebastian, Bremen) := false\n", + " travel_cost(Basel, Basel) := 0\n", + " travel_cost(Prague, Prague) := 0\n", + " travel_cost(Trento, Trento) := 0\n", + " travel_cost(Toulouse, Toulouse) := 0\n", + " travel_cost(Bremen, Bremen) := 0\n", + "]\n", + "\n", + "goals = [\n", + " Forall (Attendee - Person a) Exists (Location destination_location) (at(a, destination_location) and destination(a, destination_location))\n", + "]\n", + "\n", + "quality metrics = [\n", + " minimize actions-cost: {'travel': travel_cost(l_from, l_to), 'default': None}\n", + "]\n" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "utr_problem = utr_result.problem\n", + "utr_problem" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "functools.partial(, map={action travel(Person p, Location l_from, Location l_to) {\n", + " preconditions = [\n", + " at(p, l_from)\n", + " ]\n", + " effects = [\n", + " if (l_to == Basel) then at(p, Basel) := true\n", + " if (not (l_to == Basel)) then at(p, Basel) := false\n", + " if (l_to == Prague) then at(p, Prague) := true\n", + " if (not (l_to == Prague)) then at(p, Prague) := false\n", + " if (l_to == Trento) then at(p, Trento) := true\n", + " if (not (l_to == Trento)) then at(p, Trento) := false\n", + " if (l_to == Toulouse) then at(p, Toulouse) := true\n", + " if (not (l_to == Toulouse)) then at(p, Toulouse) := false\n", + " if (l_to == Bremen) then at(p, Bremen) := true\n", + " if (not (l_to == Bremen)) then at(p, Bremen) := false\n", + " ]\n", + " }: action travel(Person p, Location l_from, Location l_to) {\n", + " preconditions = [\n", + " (at(p) == l_from)\n", + " ]\n", + " effects = [\n", + " at(p) := l_to\n", + " ]\n", + " }})\n" + ] + } + ], + "source": [ + "utr_map_back = utr_result.map_back_action_instance\n", + "print(utr_map_back)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Let's use this to find a plan for the original problem." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[96m\u001b[1mNOTE: To disable printing of planning engine credits, add this line to your code: `up.shortcuts.get_environment().credits_stream = None`\n", + "\u001b[0m\u001b[96m *** Credits ***\n", + "\u001b[0m\u001b[96m * In operation mode `OneshotPlanner` at line 1 of `/tmp/ipykernel_133973/1887964203.py`, \u001b[0m\u001b[96myou are using the following planning engine:\n", + "\u001b[0m\u001b[96m * Engine name: Fast Downward\n", + " * Developers: Uni Basel team and contributors (cf. https://github.com/aibasel/downward/blob/main/README.md)\n", + "\u001b[0m\u001b[96m * Description: \u001b[0m\u001b[96mFast Downward is a domain-independent classical planning system.\u001b[0m\u001b[96m\n", + "\u001b[0m\u001b[96m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "with OneshotPlanner(problem_kind=utr_problem.kind) as planner:\n", + " res = planner.solve(utr_problem)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res.status" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "SequentialPlan([travel(Sebastian, Bremen, Prague), travel(Gabi, Basel, Prague), travel(Arthur, Toulouse, Prague), travel(Andrea, Trento, Prague)])" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "utr_plan = res.plan\n", + "utr_plan" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "This is a plan for the compiled task. To transform it into a task for the original problem, we need to map the actions back (not immediately visible because the actions have the same names):" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "SequentialPlan([travel(Sebastian, Bremen, Prague), travel(Gabi, Basel, Prague), travel(Arthur, Toulouse, Prague), travel(Andrea, Trento, Prague)])" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plan = utr_plan.replace_action_instances(utr_map_back)\n", + "plan" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "action travel(Person p, Location l_from, Location l_to) {\n", + " preconditions = [\n", + " (at(p) == l_from)\n", + " ]\n", + " effects = [\n", + " at(p) := l_to\n", + " ]\n", + " }" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "act = plan.actions[-1] # TRY: compare last action of utr_plan and plan\n", + "act.action" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Currently supported compilation kinds:\n", + "\n", + "* GROUNDING\n", + "* CONDITIONAL_EFFECTS_REMOVING\n", + "* DISJUNCTIVE_CONDITIONS_REMOVING\n", + "* NEGATIVE_CONDITIONS_REMOVING\n", + "* QUANTIFIERS_REMOVING\n", + "* TRAJECTORY_CONSTRAINTS_REMOVING\n", + "* USERTYPE_FLUENTS_REMOVING\n", + "* BOUNDED_TYPES_REMOVING\n", + "* STATE_INVARIANTS_REMOVING\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Solving a task optimally\n", + "\n", + "Remember the result status of our PlanGenerationResult?" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res.status" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "We would like to find an optimal plan (with guarantee!). This can be requested with:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[96m *** Credits ***\n", + "\u001b[0m\u001b[96m * In operation mode `OneshotPlanner` at line 3 of `/tmp/ipykernel_133973/1418646784.py`, \u001b[0m\u001b[96myou are using the following planning engine:\n", + "\u001b[0m\u001b[96m * Engine name: SymK\n", + " * Developers: David Speck (cf. https://github.com/speckdavid/symk/blob/master/README.md )\n", + "\u001b[0m\u001b[96m * Description: \u001b[0m\u001b[96mSymK is a state-of-the-art domain-independent classical optimal and top-k planner.\u001b[0m\u001b[96m\n", + "\u001b[0m\u001b[96m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "from unified_planning.engines import PlanGenerationResultStatus\n", + "\n", + "with OneshotPlanner(problem_kind=utr_problem.kind,\n", + " optimality_guarantee=PlanGenerationResultStatus.SOLVED_OPTIMALLY) as planner:\n", + " res = planner.solve(utr_problem)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "SequentialPlan([travel(Gabi, Basel, Trento), travel(Gabi, Trento, Bremen), travel(Andrea, Trento, Bremen), travel(Andrea, Bremen, Prague), travel(Gabi, Bremen, Prague), travel(Sebastian, Bremen, Prague), travel(Arthur, Toulouse, Basel), travel(Arthur, Basel, Trento), travel(Arthur, Trento, Bremen), travel(Arthur, Bremen, Prague)])" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res.plan" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## More on Actions\n", + "\n", + "`InstantaneousAction`s can have arbitrary formulas as preconditions as well as universal and conditional effects. Let's consider another action that exploits these features.\n", + "\n", + "Formulas are most easily build using [shortcuts](https://github.com/aiplan4eu/unified-planning/blob/master/unified_planning/shortcuts.py). Here we will use `Equals`." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "action beam_persons_to_prague {\n", + " preconditions = [\n", + " Exists (Attendee - Person a) (not (at(a) == Prague))\n", + " ]\n", + " effects = [\n", + " forall Attendee - Person a if can_beam(a) then at(a) := Prague\n", + " ]\n", + " }\n" + ] + } + ], + "source": [ + "can_beam = Fluent(\"can_beam\", BoolType(), p=person)\n", + "\n", + "\n", + "beam = InstantaneousAction('beam_persons_to_prague')\n", + "# reminder: a was a variable of type person\n", + "beam.add_precondition(Exists(Not(Equals(fluent_at(a), prague)), a))\n", + "beam.add_effect(fluent_at(a), prague, condition=can_beam(a), forall=[a])\n", + "print(beam)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Getting Help\n", + "\n", + "We can use python's built-in help functionality to get help on any object." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on method add_effect in module unified_planning.model.action:\n", + "\n", + "add_effect(fluent: Union[ForwardRef('up.model.fnode.FNode'), ForwardRef('up.model.fluent.Fluent')], value: 'up.model.expression.Expression', condition: 'up.model.expression.BoolExpression' = True, forall: Iterable[ForwardRef('up.model.variable.Variable')] = ()) method of unified_planning.model.action.InstantaneousAction instance\n", + " Adds the given `assignment` to the `action's effects`.\n", + " \n", + " :param fluent: The `fluent` of which `value` is modified by the `assignment`.\n", + " :param value: The `value` to assign to the given `fluent`.\n", + " :param condition: The `condition` in which this `effect` is applied; the default\n", + " value is `True`.\n", + " :param forall: The 'Variables' that are universally quantified in this\n", + " effect; the default value is empty.\n", + "\n" + ] + } + ], + "source": [ + "help(beam.add_effect) # also try beam.add_effect" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Numeric Planning" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "* No formal distinction of classical and numeric planning\n", + "* It's all in the problem kind: \n", + " if you use numeric features, you will need a numeric planner that supports them." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "integer travel_time" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "acceptable_changes = Fluent(\"acceptable_changes\", IntType(), p=person)\n", + "total_travel_time = Fluent(\"travel_time\", IntType())\n", + "problem.add_fluent(acceptable_changes, default_initial_value=2)\n", + "problem.add_fluent(total_travel_time, default_initial_value=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Extension of formulas (for preconditions and goals)\n", + "* Numeric expressions can be combined by operands +, -, *, /\n", + "* Numeric expressions can be compared with $\\le$ (`LE`), $\\ge$ (`GE`), $<$ (`LT`), $>$ (`GT`), $=$ (`Equals`)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "action travel(Person p, Location l_from, Location l_to) {\n", + " preconditions = [\n", + " (at(p) == l_from)\n", + " (1 <= acceptable_changes(p))\n", + " ]\n", + " effects = [\n", + " at(p) := l_to\n", + " ]\n", + " }\n" + ] + } + ], + "source": [ + "travel.add_precondition(acceptable_changes(p) >= 1)\n", + "print(travel)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Besides the normal (assignment) effects, we can also have increase and decrease effects:" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "action travel(Person p, Location l_from, Location l_to) {\n", + " preconditions = [\n", + " (at(p) == l_from)\n", + " (1 <= acceptable_changes(p))\n", + " ]\n", + " effects = [\n", + " at(p) := l_to\n", + " acceptable_changes(p) -= 1\n", + " travel_time += travel_cost(l_from, l_to)\n", + " ]\n", + " }\n" + ] + } + ], + "source": [ + "travel.add_decrease_effect(acceptable_changes(travel.p), 1)\n", + "travel.add_increase_effect(total_travel_time(), travel_cost(travel.l_from, travel.l_to))\n", + "print(travel)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Let's also use a new optimization metric..." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "maximize ((((acceptable_changes(Andrea) + acceptable_changes(Sebastian)) + acceptable_changes(Arthur)) + acceptable_changes(Gabi)) - (travel_time / 100))" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "problem.clear_quality_metrics()\n", + "m = MaximizeExpressionOnFinalState(acceptable_changes(problem.object(\"Andrea\")) +\n", + " acceptable_changes(problem.object(\"Sebastian\")) +\n", + " acceptable_changes(problem.object(\"Arthur\")) +\n", + " acceptable_changes(problem.object(\"Gabi\")) -\n", + " total_travel_time()/100)\n", + "problem.add_quality_metric(m)\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[96m *** Credits ***\n", + "\u001b[0m\u001b[96m * In operation mode `OneshotPlanner` at line 10 of `/tmp/ipykernel_133973/277381419.py`, \u001b[0m\u001b[96myou are using the following planning engine:\n", + "\u001b[0m\u001b[96m * Engine name: ENHSP\n", + " * Developers: Enrico Scala\n", + "\u001b[0m\u001b[96m * Description: \u001b[0m\u001b[96mExpressive Numeric Heuristic Search Planner.\u001b[0m\u001b[96m\n", + "\u001b[0m\u001b[96m\n", + "\u001b[0mstatus: SOLVED_SATISFICING\n", + "engine: SAT-enhsp\n", + "plan: SequentialPlan:\n", + " travel(Gabi, Basel, Prague)\n", + " travel(Arthur, Toulouse, Prague)\n", + " travel(Andrea, Trento, Prague)\n", + " travel(Sebastian, Bremen, Prague)\n" + ] + } + ], + "source": [ + "with Compiler(\n", + " problem_kind = problem.kind,\n", + " compilation_kind = CompilationKind.USERTYPE_FLUENTS_REMOVING\n", + " ) as usertype_remover:\n", + " utr_result = usertype_remover.compile(\n", + " problem,\n", + " CompilationKind.USERTYPE_FLUENTS_REMOVING\n", + " )\n", + "utr_problem = utr_result.problem\n", + "with OneshotPlanner(problem_kind=utr_problem.kind) as planner:\n", + " res = planner.solve(utr_problem)\n", + " print(res) " + ] + } + ], + "metadata": { + "celltoolbar": "Tags", + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} From 8a22a66e11292bc79d1bc2f5616fecd1e9285f9b Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Thu, 9 Nov 2023 13:40:18 +0100 Subject: [PATCH 07/19] doc: add planner integration doc to engines section --- docs/engines.rst | 5 ++++- docs/engines/03_integrating_engine.rst | 7 ------- docs/notebooks/{ => tutorial}/planner-integration.ipynb | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) delete mode 100644 docs/engines/03_integrating_engine.rst rename docs/notebooks/{ => tutorial}/planner-integration.ipynb (99%) diff --git a/docs/engines.rst b/docs/engines.rst index d5e479cc6..2150ed60d 100644 --- a/docs/engines.rst +++ b/docs/engines.rst @@ -9,4 +9,7 @@ The ``Engine`` class is the class interface that has to be implemented in order :hidden: :caption: Getting Started - engines/* \ No newline at end of file + engines/01_available_engines.rst + engines/02_engine_selection.rst + notebooks/tutorial/planner-integration.ipynb + engines/04_testing_engine.rst \ No newline at end of file diff --git a/docs/engines/03_integrating_engine.rst b/docs/engines/03_integrating_engine.rst deleted file mode 100644 index 69e4f62e1..000000000 --- a/docs/engines/03_integrating_engine.rst +++ /dev/null @@ -1,7 +0,0 @@ -Integrating an Engine -===================== - -TODO: populate with ICAPS tutorial notebook - - - diff --git a/docs/notebooks/planner-integration.ipynb b/docs/notebooks/tutorial/planner-integration.ipynb similarity index 99% rename from docs/notebooks/planner-integration.ipynb rename to docs/notebooks/tutorial/planner-integration.ipynb index b198ad71a..2b739e0f7 100644 --- a/docs/notebooks/planner-integration.ipynb +++ b/docs/notebooks/tutorial/planner-integration.ipynb @@ -208,7 +208,7 @@ "source": [ "Essentially, we just need to give a custom name (in our case `yoloplanner`) a module name (in thic case, `__name__` as we are in the same file as the Solver class) and finally the class name that we used to define our planning engine.\n", "\n", - "Done! We are nor ready to test our planning engine!" + "Done! We are now ready to test our planning engine!" ] }, { From e7994266fb48ef930d4bb43ae0884fb2c8d773bb Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Thu, 9 Nov 2023 13:53:09 +0100 Subject: [PATCH 08/19] doc: fix pre-1.0 leftovers in notebooks --- docs/notebooks/01-basic-example.ipynb | 2 +- docs/notebooks/01-numeric-planning.ipynb | 2 +- docs/notebooks/02-optimal-planning.ipynb | 2 +- docs/notebooks/03-temporal-planning.ipynb | 2 +- docs/notebooks/04-simulated-effects.ipynb | 2 +- docs/notebooks/05-compilers.ipynb | 2 +- .../06-oversubscription-with-metaengine.ipynb | 2 +- docs/notebooks/08-sequential-simulator.ipynb | 2 +- .../09-multiagent-planning-simple.ipynb | 2 +- docs/notebooks/10-multiagent-planning.ipynb | 2 +- docs/notebooks/11-plot.ipynb | 2 +- .../12-plan-parsing-conversion.ipynb | 2 +- docs/notebooks/tutorial/modeling.ipynb | 721 ++++++++---------- .../tutorial/planner-integration.ipynb | 2 +- 14 files changed, 347 insertions(+), 400 deletions(-) diff --git a/docs/notebooks/01-basic-example.ipynb b/docs/notebooks/01-basic-example.ipynb index 7be2c0732..6d7e1309d 100644 --- a/docs/notebooks/01-basic-example.ipynb +++ b/docs/notebooks/01-basic-example.ipynb @@ -52,7 +52,7 @@ "outputs": [], "source": [ "!apt install graphviz graphviz-dev\n", - "!pip install --pre unified-planning[pyperplan,tamer,plot]" + "%pip install unified-planning[pyperplan,tamer,plot]" ] }, { diff --git a/docs/notebooks/01-numeric-planning.ipynb b/docs/notebooks/01-numeric-planning.ipynb index 944fb2e60..80e052b63 100644 --- a/docs/notebooks/01-numeric-planning.ipynb +++ b/docs/notebooks/01-numeric-planning.ipynb @@ -42,7 +42,7 @@ "outputs": [], "source": [ "!apt-get install openjdk-17-jdk\n", - "!pip install --pre unified-planning[enhsp]" + "%pip install unified-planning[enhsp]" ] }, { diff --git a/docs/notebooks/02-optimal-planning.ipynb b/docs/notebooks/02-optimal-planning.ipynb index 1ddaece0f..3a79c5320 100644 --- a/docs/notebooks/02-optimal-planning.ipynb +++ b/docs/notebooks/02-optimal-planning.ipynb @@ -36,7 +36,7 @@ }, "outputs": [], "source": [ - "!pip install --pre unified-planning[fast-downward]" + "%pip install unified-planning[fast-downward]" ] }, { diff --git a/docs/notebooks/03-temporal-planning.ipynb b/docs/notebooks/03-temporal-planning.ipynb index 202a48456..36642d294 100644 --- a/docs/notebooks/03-temporal-planning.ipynb +++ b/docs/notebooks/03-temporal-planning.ipynb @@ -37,7 +37,7 @@ "outputs": [], "source": [ "!apt install graphviz graphviz-dev\n", - "!pip install --pre unified-planning[tamer,plot]" + "%pip install unified-planning[tamer,plot]" ] }, { diff --git a/docs/notebooks/04-simulated-effects.ipynb b/docs/notebooks/04-simulated-effects.ipynb index 89efa67b4..06fedbf7d 100644 --- a/docs/notebooks/04-simulated-effects.ipynb +++ b/docs/notebooks/04-simulated-effects.ipynb @@ -42,7 +42,7 @@ }, "outputs": [], "source": [ - "!pip install --pre unified-planning[tamer]" + "%pip install unified-planning[tamer]" ] }, { diff --git a/docs/notebooks/05-compilers.ipynb b/docs/notebooks/05-compilers.ipynb index d9ef1ffdb..e268876cb 100644 --- a/docs/notebooks/05-compilers.ipynb +++ b/docs/notebooks/05-compilers.ipynb @@ -30,7 +30,7 @@ "outputs": [], "source": [ "!apt install graphviz graphviz-dev\n", - "!pip install --pre unified-planning[tamer,plot]" + "%pip install unified-planning[tamer,plot]" ] }, { diff --git a/docs/notebooks/06-oversubscription-with-metaengine.ipynb b/docs/notebooks/06-oversubscription-with-metaengine.ipynb index 5fc529bf3..064759857 100644 --- a/docs/notebooks/06-oversubscription-with-metaengine.ipynb +++ b/docs/notebooks/06-oversubscription-with-metaengine.ipynb @@ -44,7 +44,7 @@ "outputs": [], "source": [ "!apt install graphviz graphviz-dev\n", - "!pip install --pre unified-planning[tamer,plot]" + "%pip install unified-planning[tamer,plot]" ] }, { diff --git a/docs/notebooks/08-sequential-simulator.ipynb b/docs/notebooks/08-sequential-simulator.ipynb index 1d0a310fd..1a8cf7c75 100644 --- a/docs/notebooks/08-sequential-simulator.ipynb +++ b/docs/notebooks/08-sequential-simulator.ipynb @@ -47,7 +47,7 @@ "outputs": [], "source": [ "!apt install graphviz graphviz-dev\n", - "!pip install --pre unified-planning[plot]" + "%pip install unified-planning[plot]" ] }, { diff --git a/docs/notebooks/09-multiagent-planning-simple.ipynb b/docs/notebooks/09-multiagent-planning-simple.ipynb index 1ba094d75..1d57297dc 100644 --- a/docs/notebooks/09-multiagent-planning-simple.ipynb +++ b/docs/notebooks/09-multiagent-planning-simple.ipynb @@ -41,7 +41,7 @@ }, "outputs": [], "source": [ - "!pip install --pre unified-planning[fmap]" + "!pip install unified-planning[fmap]" ] }, { diff --git a/docs/notebooks/10-multiagent-planning.ipynb b/docs/notebooks/10-multiagent-planning.ipynb index 4ccef4d4f..73b05c3d5 100644 --- a/docs/notebooks/10-multiagent-planning.ipynb +++ b/docs/notebooks/10-multiagent-planning.ipynb @@ -49,7 +49,7 @@ "outputs": [], "source": [ "!apt install graphviz graphviz-dev\n", - "!pip install --pre unified-planning[fmap,plot]" + "%pip install unified-planning[fmap,plot]" ] }, { diff --git a/docs/notebooks/11-plot.ipynb b/docs/notebooks/11-plot.ipynb index 493de5eb9..b37be01ee 100644 --- a/docs/notebooks/11-plot.ipynb +++ b/docs/notebooks/11-plot.ipynb @@ -38,7 +38,7 @@ "outputs": [], "source": [ "!apt install graphviz graphviz-dev\n", - "!pip install --pre unified-planning[plot]" + "%pip install unified-planning[plot]" ] }, { diff --git a/docs/notebooks/12-plan-parsing-conversion.ipynb b/docs/notebooks/12-plan-parsing-conversion.ipynb index 8594330fc..8decbc580 100644 --- a/docs/notebooks/12-plan-parsing-conversion.ipynb +++ b/docs/notebooks/12-plan-parsing-conversion.ipynb @@ -41,7 +41,7 @@ }, "outputs": [], "source": [ - "!pip install --pre unified-planning" + "%pip install unified-planning" ] }, { diff --git a/docs/notebooks/tutorial/modeling.ipynb b/docs/notebooks/tutorial/modeling.ipynb index ba699d7ed..f19fad07c 100644 --- a/docs/notebooks/tutorial/modeling.ipynb +++ b/docs/notebooks/tutorial/modeling.ipynb @@ -9,7 +9,9 @@ } }, "source": [ - "# Modelling and Solving Planning Tasks" + "# Modelling and Solving Planning Tasks\n", + "\n", + "This tutorial proposes to explore the various features of the *unified-planning* library. The focus is deliberately kept on the most common planning problems (classical and numeric) to ease the presentation." ] }, { @@ -26,7 +28,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -35,49 +37,14 @@ "outputId": "62058f78-3dfc-4be5-ed71-ea97b9ffad27", "slideshow": { "slide_type": "fragment" - } + }, + "tags": [ + "remove_from_CI" + ] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Defaulting to user installation because normal site-packages is not writeable\n", - "Requirement already satisfied: unified-planning[engines] in /home/roeger/.local/lib/python3.10/site-packages (1.0.0)\n", - "Requirement already satisfied: pyparsing in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (3.1.0a1)\n", - "Requirement already satisfied: networkx in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (2.8)\n", - "Requirement already satisfied: up-fast-downward==0.2.3 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (0.2.3)\n", - "Requirement already satisfied: up-lpg==0.0.7 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (0.0.7)\n", - "Requirement already satisfied: up-aries>=0.0.8 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (0.1.0)\n", - "Requirement already satisfied: up-enhsp==0.0.15 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (0.0.15)\n", - "Requirement already satisfied: tarski[arithmetic] in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (0.8.2)\n", - "Requirement already satisfied: up-tamer==1.0.0 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (1.0.0)\n", - "Requirement already satisfied: up-pyperplan==1.0.0 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (1.0.0)\n", - "Requirement already satisfied: up-symk>=0.0.3 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (0.0.4)\n", - "Requirement already satisfied: up-fmap==0.0.7 in /home/roeger/.local/lib/python3.10/site-packages (from unified-planning[engines]) (0.0.7)\n", - "Requirement already satisfied: pyperplan==2.1 in /home/roeger/.local/lib/python3.10/site-packages (from up-pyperplan==1.0.0->unified-planning[engines]) (2.1)\n", - "Requirement already satisfied: pytamer==0.1.15 in /home/roeger/.local/lib/python3.10/site-packages (from up-tamer==1.0.0->unified-planning[engines]) (0.1.15)\n", - "Requirement already satisfied: wheel in /usr/lib/python3/dist-packages (from pyperplan==2.1->up-pyperplan==1.0.0->unified-planning[engines]) (0.37.1)\n", - "Requirement already satisfied: grpcio in /home/roeger/.local/lib/python3.10/site-packages (from up-aries>=0.0.8->unified-planning[engines]) (1.56.0)\n", - "Requirement already satisfied: pytest in /home/roeger/.local/lib/python3.10/site-packages (from up-aries>=0.0.8->unified-planning[engines]) (7.4.0)\n", - "Requirement already satisfied: grpcio-tools in /home/roeger/.local/lib/python3.10/site-packages (from up-aries>=0.0.8->unified-planning[engines]) (1.56.0)\n", - "Requirement already satisfied: psutil in /home/roeger/.local/lib/python3.10/site-packages (from tarski[arithmetic]->unified-planning[engines]) (5.9.5)\n", - "Requirement already satisfied: antlr4-python3-runtime==4.7.2 in /home/roeger/.local/lib/python3.10/site-packages (from tarski[arithmetic]->unified-planning[engines]) (4.7.2)\n", - "Requirement already satisfied: multipledispatch in /home/roeger/.local/lib/python3.10/site-packages (from tarski[arithmetic]->unified-planning[engines]) (1.0.0)\n", - "Requirement already satisfied: numpy in /usr/lib/python3/dist-packages (from tarski[arithmetic]->unified-planning[engines]) (1.21.5)\n", - "Requirement already satisfied: scipy in /usr/lib/python3/dist-packages (from tarski[arithmetic]->unified-planning[engines]) (1.8.0)\n", - "Requirement already satisfied: protobuf<5.0dev,>=4.21.6 in /home/roeger/.local/lib/python3.10/site-packages (from grpcio-tools->up-aries>=0.0.8->unified-planning[engines]) (4.23.3)\n", - "Requirement already satisfied: setuptools in /usr/lib/python3/dist-packages (from grpcio-tools->up-aries>=0.0.8->unified-planning[engines]) (59.6.0)\n", - "Requirement already satisfied: packaging in /usr/lib/python3/dist-packages (from pytest->up-aries>=0.0.8->unified-planning[engines]) (21.3)\n", - "Requirement already satisfied: pluggy<2.0,>=0.12 in /usr/lib/python3/dist-packages (from pytest->up-aries>=0.0.8->unified-planning[engines]) (0.13.0)\n", - "Requirement already satisfied: tomli>=1.0.0 in /home/roeger/.local/lib/python3.10/site-packages (from pytest->up-aries>=0.0.8->unified-planning[engines]) (2.0.1)\n", - "Requirement already satisfied: iniconfig in /home/roeger/.local/lib/python3.10/site-packages (from pytest->up-aries>=0.0.8->unified-planning[engines]) (2.0.0)\n", - "Requirement already satisfied: exceptiongroup>=1.0.0rc8 in /home/roeger/.local/lib/python3.10/site-packages (from pytest->up-aries>=0.0.8->unified-planning[engines]) (1.1.0)\n" - ] - } - ], + "outputs": [], "source": [ - "!pip install unified-planning[engines]" + "%pip install unified-planning[engines]" ] }, { @@ -95,7 +62,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": { "id": "aX_XEsUcKzIP", "slideshow": { @@ -156,7 +123,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": { "id": "XZtPHmYnQ4Ob", "slideshow": { @@ -173,7 +140,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": { "id": "nbxlmbFxQ14f", "slideshow": { @@ -204,7 +171,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": { "id": "sPbtqMo_Qf-k", "slideshow": { @@ -231,7 +198,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": { "id": "yXOUGV0cqKUc", "slideshow": { @@ -246,7 +213,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": { "slideshow": { "slide_type": "fragment" @@ -254,23 +221,20 @@ }, "outputs": [ { - "data": { - "text/plain": [ - "unified_planning.model.fnode.FNode" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] } ], "source": [ - "arrived_gabi.__class__" + "print(arrived_gabi.__class__)" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": { "slideshow": { "slide_type": "fragment" @@ -283,7 +247,7 @@ "bool arrived[person=Attendee - Person]" ] }, - "execution_count": 8, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -294,7 +258,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "metadata": { "slideshow": { "slide_type": "fragment" @@ -307,7 +271,7 @@ "(Gabi,)" ] }, - "execution_count": 9, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -335,12 +299,12 @@ } }, "source": [ - "For classical, numeric and temporal planning, we use class [`Problem`](https://unified-planning.readthedocs.io/en/latest/api/model/Problem.html). " + "For classical, numeric and temporal planning, we use the class [`Problem`](https://unified-planning.readthedocs.io/en/latest/api/model/Problem.html). " ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "metadata": { "slideshow": { "slide_type": "fragment" @@ -366,7 +330,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "metadata": { "slideshow": { "slide_type": "fragment" @@ -379,7 +343,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 11, "metadata": { "slideshow": { "slide_type": "fragment" @@ -392,13 +356,13 @@ "Location at[p=Person]" ] }, - "execution_count": 12, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# You can also get hold of a fluent later:\n", + "# You can also get hold of a fluent from its name:\n", "problem.fluent(\"at\")" ] }, @@ -415,7 +379,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "metadata": { "slideshow": { "slide_type": "fragment" @@ -430,7 +394,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "metadata": { "slideshow": { "slide_type": "fragment" @@ -443,7 +407,7 @@ "[Gabi, Andrea, Arthur, Sebastian]" ] }, - "execution_count": 14, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -454,7 +418,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 14, "metadata": { "slideshow": { "slide_type": "fragment" @@ -487,7 +451,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 15, "metadata": { "slideshow": { "slide_type": "slide" @@ -495,44 +459,43 @@ }, "outputs": [ { - "data": { - "text/plain": [ - "problem name = Travel to ICAPS\n", - "\n", - "types = [Location, Person, Attendee - Person]\n", - "\n", - "fluents = [\n", - " Location destination[person=Attendee - Person]\n", - " Location at[p=Person]\n", - "]\n", - "\n", - "actions = [\n", - "]\n", - "\n", - "objects = [\n", - " Location: [Basel, Prague, Trento, Toulouse, Bremen]\n", - " Person: [Gabi, Mum, Andrea, Arthur, Sebastian]\n", - " Attendee - Person: [Gabi, Andrea, Arthur, Sebastian]\n", - "]\n", - "\n", - "initial fluents default = [\n", - " Location destination[person=Attendee - Person] := Prague\n", - "]\n", - "\n", - "initial values = [\n", - "]\n", - "\n", - "goals = [\n", - "]\n" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "problem name = Travel to ICAPS\n", + "\n", + "types = [Location, Person, Attendee - Person]\n", + "\n", + "fluents = [\n", + " Location destination[person=Attendee - Person]\n", + " Location at[p=Person]\n", + "]\n", + "\n", + "actions = [\n", + "]\n", + "\n", + "objects = [\n", + " Location: [Basel, Prague, Trento, Toulouse, Bremen]\n", + " Person: [Gabi, Mum, Andrea, Arthur, Sebastian]\n", + " Attendee - Person: [Gabi, Andrea, Arthur, Sebastian]\n", + "]\n", + "\n", + "initial fluents default = [\n", + " Location destination[person=Attendee - Person] := Prague\n", + "]\n", + "\n", + "initial values = [\n", + "]\n", + "\n", + "goals = [\n", + "]\n", + "\n", + "\n" + ] } ], "source": [ - "problem" + "print(problem)" ] }, { @@ -548,7 +511,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 16, "metadata": { "slideshow": { "slide_type": "fragment" @@ -564,7 +527,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 17, "metadata": { "slideshow": { "slide_type": "slide" @@ -572,49 +535,48 @@ }, "outputs": [ { - "data": { - "text/plain": [ - "problem name = Travel to ICAPS\n", - "\n", - "types = [Location, Person, Attendee - Person]\n", - "\n", - "fluents = [\n", - " Location destination[person=Attendee - Person]\n", - " Location at[p=Person]\n", - "]\n", - "\n", - "actions = [\n", - "]\n", - "\n", - "objects = [\n", - " Location: [Basel, Prague, Trento, Toulouse, Bremen]\n", - " Person: [Gabi, Mum, Andrea, Arthur, Sebastian]\n", - " Attendee - Person: [Gabi, Andrea, Arthur, Sebastian]\n", - "]\n", - "\n", - "initial fluents default = [\n", - " Location destination[person=Attendee - Person] := Prague\n", - "]\n", - "\n", - "initial values = [\n", - " at(Andrea) := Trento\n", - " at(Arthur) := Toulouse\n", - " at(Sebastian) := Bremen\n", - " at(Gabi) := Basel\n", - " at(Mum) := Basel\n", - "]\n", - "\n", - "goals = [\n", - "]\n" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "problem name = Travel to ICAPS\n", + "\n", + "types = [Location, Person, Attendee - Person]\n", + "\n", + "fluents = [\n", + " Location destination[person=Attendee - Person]\n", + " Location at[p=Person]\n", + "]\n", + "\n", + "actions = [\n", + "]\n", + "\n", + "objects = [\n", + " Location: [Basel, Prague, Trento, Toulouse, Bremen]\n", + " Person: [Gabi, Mum, Andrea, Arthur, Sebastian]\n", + " Attendee - Person: [Gabi, Andrea, Arthur, Sebastian]\n", + "]\n", + "\n", + "initial fluents default = [\n", + " Location destination[person=Attendee - Person] := Prague\n", + "]\n", + "\n", + "initial values = [\n", + " at(Andrea) := Trento\n", + " at(Arthur) := Toulouse\n", + " at(Sebastian) := Bremen\n", + " at(Gabi) := Basel\n", + " at(Mum) := Basel\n", + "]\n", + "\n", + "goals = [\n", + "]\n", + "\n", + "\n" + ] } ], "source": [ - "problem" + "print(problem)" ] }, { @@ -632,7 +594,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 18, "metadata": { "slideshow": { "slide_type": "fragment" @@ -640,21 +602,18 @@ }, "outputs": [ { - "data": { - "text/plain": [ - "[Forall (Attendee - Person a) (at(a) == destination(a))]" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "[Forall (Attendee - Person a) (at(a) == destination(a))]\n" + ] } ], "source": [ "a = Variable(\"a\", conference_attendee)\n", "problem.add_goal(Forall(fluent_at(a).Equals(destination(a)), a))\n", "\n", - "problem.goals" + "print(problem.goals)" ] }, { @@ -672,7 +631,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 19, "metadata": { "slideshow": { "slide_type": "fragment" @@ -680,21 +639,18 @@ }, "outputs": [ { - "data": { - "text/plain": [ - "[action travel(Person p, Location l_from, Location l_to) {\n", - " preconditions = [\n", - " (at(p) == l_from)\n", - " ]\n", - " effects = [\n", - " at(p) := l_to\n", - " ]\n", - " }]" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "[action travel(Person p, Location l_from, Location l_to) {\n", + " preconditions = [\n", + " (at(p) == l_from)\n", + " ]\n", + " effects = [\n", + " at(p) := l_to\n", + " ]\n", + " }]\n" + ] } ], "source": [ @@ -705,7 +661,7 @@ "travel.add_precondition(fluent_at(p).Equals(l_from))\n", "travel.add_effect(fluent_at(p), l_to)\n", "problem.add_action(travel)\n", - "problem.actions" + "print(problem.actions)" ] }, { @@ -725,7 +681,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 20, "metadata": { "slideshow": { "slide_type": "fragment" @@ -760,7 +716,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 21, "metadata": { "slideshow": { "slide_type": "fragment" @@ -789,7 +745,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 22, "metadata": { "slideshow": { "slide_type": "fragment" @@ -797,18 +753,20 @@ }, "outputs": [ { - "data": { - "text/plain": [ - "ProblemKind(['UNIVERSAL_CONDITIONS', 'EQUALITIES', 'ACTIONS_COST', 'ACTION_BASED', 'HIERARCHICAL_TYPING', 'FLAT_TYPING', 'STATIC_FLUENTS_IN_ACTIONS_COST', 'OBJECT_FLUENTS'])" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "PROBLEM_CLASS: ['ACTION_BASED']\n", + "CONDITIONS_KIND: ['UNIVERSAL_CONDITIONS', 'EQUALITIES']\n", + "TYPING: ['FLAT_TYPING', 'HIERARCHICAL_TYPING']\n", + "FLUENTS_TYPE: ['OBJECT_FLUENTS']\n", + "QUALITY_METRICS: ['ACTIONS_COST']\n", + "ACTIONS_COST_KIND: ['INT_NUMBERS_IN_ACTIONS_COST', 'STATIC_FLUENTS_IN_ACTIONS_COST']\n" + ] } ], "source": [ - "problem.kind" + "print(problem.kind)" ] }, { @@ -819,45 +777,37 @@ } }, "source": [ - "It's time to solve this incredibly hard problem..." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "source": [ - "But here at ICAPS we know that object fluents are not widely supported by planning engines... And indeed:" + "It's time to solve this incredibly hard problem...\n", + "\n", + "but of course, object fluents are not widely supported by planning engines... And indeed:" ] }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 23, "metadata": { "slideshow": { "slide_type": "fragment" }, "tags": [ - "raises-exception" + "raises-exception", + "remove_from_CI" ] }, "outputs": [ { "ename": "UPNoSuitableEngineAvailableException", - "evalue": "No available engine supports all the problem features:\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| Engine | UNIVERSAL_CONDITIONS | EQUALITIES | ACTIONS_COST | ACTION_BASED | HIERARCHICAL_TYPING | FLAT_TYPING | STATIC_FLUENTS_IN_ACTIONS_COST | OBJECT_FLUENTS |\n===============================================================================================================================================================================================\n| fast-downward | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| fast-downward-opt | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| symk | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| symk-opt | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| pyperplan | False | False | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| pyperplan-opt | False | False | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp-opt | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp-any | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| tamer | False | True | False | True | False | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg | False | True | False | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg-anytime | False | True | False | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg-repairer | False | True | False | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| fmap | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| aries | False | True | True | True | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[fast-downward] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[fast-downward-opt] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[symk] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[symk-opt] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp-opt] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp-any] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[tamer] | False | True | False | True | False | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[aries] | False | True | False | True | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------", + "evalue": "No available engine supports all the problem features:\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| Engine | FLAT_TYPING | UNIVERSAL_CONDITIONS | INT_NUMBERS_IN_ACTIONS_COST | STATIC_FLUENTS_IN_ACTIONS_COST | OBJECT_FLUENTS | HIERARCHICAL_TYPING | ACTION_BASED | ACTIONS_COST | EQUALITIES |\n=============================================================================================================================================================================================================================\n| fast-downward | True | True | True | True | False | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| fast-downward-opt | True | True | True | True | False | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| symk | True | True | True | True | False | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| symk-opt | True | True | True | True | False | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| pyperplan | True | False | True | False | False | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| pyperplan-opt | True | False | True | False | False | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp | True | True | True | True | False | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp-opt | True | True | True | True | False | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp-any | True | True | True | True | False | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| tamer | True | False | True | False | True | False | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg | True | False | True | True | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg-anytime | True | False | True | True | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg-repairer | True | False | True | True | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| fmap | True | True | True | False | True | True | False | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| aries | True | False | True | True | True | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[fast-downward] | True | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[fast-downward-opt] | True | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[symk] | True | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[symk-opt] | True | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp] | True | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp-opt] | True | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp-any] | True | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[tamer] | True | False | True | False | True | False | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[aries] | True | False | False | False | True | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mUPNoSuitableEngineAvailableException\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/tmp/ipykernel_133973/3821768881.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mwith\u001b[0m \u001b[0mOneshotPlanner\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mproblem_kind\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mproblem\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkind\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mplanner\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mres\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mplanner\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msolve\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mproblem\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mres\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.10/site-packages/unified_planning/shortcuts.py\u001b[0m in \u001b[0;36mOneshotPlanner\u001b[0;34m(name, names, params, problem_kind, optimality_guarantee)\u001b[0m\n\u001b[1;32m 547\u001b[0m \u001b[0;34m|\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0mOneshotPlanner\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mproblem_kind\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mproblem\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkind\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moptimality_guarantee\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mSOLVED_OPTIMALLY\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 548\u001b[0m \"\"\"\n\u001b[0;32m--> 549\u001b[0;31m return get_environment().factory.OneshotPlanner(\n\u001b[0m\u001b[1;32m 550\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 551\u001b[0m \u001b[0mnames\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnames\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.10/site-packages/unified_planning/engines/factory.py\u001b[0m in \u001b[0;36mOneshotPlanner\u001b[0;34m(self, name, names, params, problem_kind, optimality_guarantee)\u001b[0m\n\u001b[1;32m 744\u001b[0m \u001b[0;34mf\"{optimality_guarantee} is not a valid OptimalityGuarantee.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 745\u001b[0m )\n\u001b[0;32m--> 746\u001b[0;31m return self._get_engine(\n\u001b[0m\u001b[1;32m 747\u001b[0m \u001b[0mOperationMode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mONESHOT_PLANNER\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 748\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.10/site-packages/unified_planning/engines/factory.py\u001b[0m in \u001b[0;36m_get_engine\u001b[0;34m(self, operation_mode, name, names, params, problem_kind, optimality_guarantee, compilation_kind, compilation_kinds, plan_kind, anytime_guarantee, problem)\u001b[0m\n\u001b[1;32m 654\u001b[0m \u001b[0mparams\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 655\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mDict\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 656\u001b[0;31m EngineClass = self._get_engine_class(\n\u001b[0m\u001b[1;32m 657\u001b[0m \u001b[0moperation_mode\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 658\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.10/site-packages/unified_planning/engines/factory.py\u001b[0m in \u001b[0;36m_get_engine_class\u001b[0;34m(self, operation_mode, name, problem_kind, optimality_guarantee, compilation_kind, plan_kind, anytime_guarantee)\u001b[0m\n\u001b[1;32m 541\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 542\u001b[0m \u001b[0mmsg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34mf\"No available {operation_mode} engine\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 543\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mup\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexceptions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mUPNoSuitableEngineAvailableException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 544\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 545\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_print_credits\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mall_credits\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mSequence\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mOptional\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"up.engines.Credits\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mUPNoSuitableEngineAvailableException\u001b[0m: No available engine supports all the problem features:\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| Engine | UNIVERSAL_CONDITIONS | EQUALITIES | ACTIONS_COST | ACTION_BASED | HIERARCHICAL_TYPING | FLAT_TYPING | STATIC_FLUENTS_IN_ACTIONS_COST | OBJECT_FLUENTS |\n===============================================================================================================================================================================================\n| fast-downward | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| fast-downward-opt | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| symk | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| symk-opt | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| pyperplan | False | False | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| pyperplan-opt | False | False | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp-opt | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp-any | True | True | True | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| tamer | False | True | False | True | False | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg | False | True | False | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg-anytime | False | True | False | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg-repairer | False | True | False | True | True | True | True | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| fmap | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| aries | False | True | True | True | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[fast-downward] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[fast-downward-opt] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[symk] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[symk-opt] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp-opt] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp-any] | True | True | False | True | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[tamer] | False | True | False | True | False | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[aries] | False | True | False | True | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------" + "\u001b[0;32m/tmp/ipykernel_173045/3821768881.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mwith\u001b[0m \u001b[0mOneshotPlanner\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mproblem_kind\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mproblem\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkind\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mplanner\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mres\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mplanner\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msolve\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mproblem\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mres\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/work/aries/planning/unified/deps/unified-planning/unified_planning/shortcuts.py\u001b[0m in \u001b[0;36mOneshotPlanner\u001b[0;34m(name, names, params, problem_kind, optimality_guarantee)\u001b[0m\n\u001b[1;32m 548\u001b[0m \u001b[0;34m|\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0mOneshotPlanner\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mproblem_kind\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mproblem\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkind\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moptimality_guarantee\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mSOLVED_OPTIMALLY\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 549\u001b[0m \"\"\"\n\u001b[0;32m--> 550\u001b[0;31m return get_environment().factory.OneshotPlanner(\n\u001b[0m\u001b[1;32m 551\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 552\u001b[0m \u001b[0mnames\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnames\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/work/aries/planning/unified/deps/unified-planning/unified_planning/engines/factory.py\u001b[0m in \u001b[0;36mOneshotPlanner\u001b[0;34m(self, name, names, params, problem_kind, optimality_guarantee)\u001b[0m\n\u001b[1;32m 752\u001b[0m \u001b[0;34mf\"{optimality_guarantee} is not a valid OptimalityGuarantee.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 753\u001b[0m )\n\u001b[0;32m--> 754\u001b[0;31m return self._get_engine(\n\u001b[0m\u001b[1;32m 755\u001b[0m \u001b[0mOperationMode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mONESHOT_PLANNER\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 756\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/work/aries/planning/unified/deps/unified-planning/unified_planning/engines/factory.py\u001b[0m in \u001b[0;36m_get_engine\u001b[0;34m(self, operation_mode, name, names, params, problem_kind, optimality_guarantee, compilation_kind, compilation_kinds, plan_kind, anytime_guarantee, problem)\u001b[0m\n\u001b[1;32m 657\u001b[0m \u001b[0mparams\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 658\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mDict\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 659\u001b[0;31m EngineClass = self._get_engine_class(\n\u001b[0m\u001b[1;32m 660\u001b[0m \u001b[0moperation_mode\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 661\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/work/aries/planning/unified/deps/unified-planning/unified_planning/engines/factory.py\u001b[0m in \u001b[0;36m_get_engine_class\u001b[0;34m(self, operation_mode, name, problem_kind, optimality_guarantee, compilation_kind, plan_kind, anytime_guarantee)\u001b[0m\n\u001b[1;32m 544\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 545\u001b[0m \u001b[0mmsg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34mf\"No available {operation_mode} engine\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 546\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mup\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexceptions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mUPNoSuitableEngineAvailableException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 547\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 548\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_print_credits\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mall_credits\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mSequence\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mOptional\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"up.engines.Credits\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mUPNoSuitableEngineAvailableException\u001b[0m: No available engine supports all the problem features:\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| Engine | FLAT_TYPING | UNIVERSAL_CONDITIONS | INT_NUMBERS_IN_ACTIONS_COST | STATIC_FLUENTS_IN_ACTIONS_COST | OBJECT_FLUENTS | HIERARCHICAL_TYPING | ACTION_BASED | ACTIONS_COST | EQUALITIES |\n=============================================================================================================================================================================================================================\n| fast-downward | True | True | True | True | False | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| fast-downward-opt | True | True | True | True | False | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| symk | True | True | True | True | False | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| symk-opt | True | True | True | True | False | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| pyperplan | True | False | True | False | False | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| pyperplan-opt | True | False | True | False | False | True | True | False | False |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp | True | True | True | True | False | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp-opt | True | True | True | True | False | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| enhsp-any | True | True | True | True | False | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| tamer | True | False | True | False | True | False | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg | True | False | True | True | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg-anytime | True | False | True | True | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| lpg-repairer | True | False | True | True | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| fmap | True | True | True | False | True | True | False | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| aries | True | False | True | True | True | True | True | True | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[fast-downward] | True | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[fast-downward-opt] | True | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[symk] | True | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[symk-opt] | True | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp] | True | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp-opt] | True | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[enhsp-any] | True | True | True | False | False | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[tamer] | True | False | True | False | True | False | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n| oversubscription[aries] | True | False | False | False | True | True | True | False | True |\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------" ] } ], @@ -877,14 +827,14 @@ "source": [ "## Using a Compiler\n", "\n", - "For Fast Downward, the only unsupported feature were the object fluents.\n", + "For Fast Downward, the only *unsupported feature* were the object fluents.\n", "\n", "Let's use a compiler to eliminate them:" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 24, "metadata": { "slideshow": { "slide_type": "fragment" @@ -904,7 +854,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 25, "metadata": { "slideshow": { "slide_type": "slide" @@ -912,142 +862,141 @@ }, "outputs": [ { - "data": { - "text/plain": [ - "problem name = utfr_Travel to ICAPS\n", - "\n", - "types = [Person, Attendee - Person, Location]\n", - "\n", - "fluents = [\n", - " bool destination[person=Attendee - Person, location=Location]\n", - " bool at[p=Person, location=Location]\n", - " integer travel_cost[from_loc=Location, to_loc=Location]\n", - "]\n", - "\n", - "actions = [\n", - " action travel(Person p, Location l_from, Location l_to) {\n", - " preconditions = [\n", - " at(p, l_from)\n", - " ]\n", - " effects = [\n", - " if (l_to == Basel) then at(p, Basel) := true\n", - " if (not (l_to == Basel)) then at(p, Basel) := false\n", - " if (l_to == Prague) then at(p, Prague) := true\n", - " if (not (l_to == Prague)) then at(p, Prague) := false\n", - " if (l_to == Trento) then at(p, Trento) := true\n", - " if (not (l_to == Trento)) then at(p, Trento) := false\n", - " if (l_to == Toulouse) then at(p, Toulouse) := true\n", - " if (not (l_to == Toulouse)) then at(p, Toulouse) := false\n", - " if (l_to == Bremen) then at(p, Bremen) := true\n", - " if (not (l_to == Bremen)) then at(p, Bremen) := false\n", - " ]\n", - " }\n", - "]\n", - "\n", - "objects = [\n", - " Person: [Gabi, Mum, Andrea, Arthur, Sebastian]\n", - " Attendee - Person: [Gabi, Andrea, Arthur, Sebastian]\n", - " Location: [Basel, Prague, Trento, Toulouse, Bremen]\n", - "]\n", - "\n", - "initial fluents default = [\n", - "]\n", - "\n", - "initial values = [\n", - " at(Andrea, Basel) := false\n", - " at(Andrea, Prague) := false\n", - " at(Andrea, Trento) := true\n", - " at(Andrea, Toulouse) := false\n", - " at(Andrea, Bremen) := false\n", - " at(Arthur, Basel) := false\n", - " at(Arthur, Prague) := false\n", - " at(Arthur, Trento) := false\n", - " at(Arthur, Toulouse) := true\n", - " at(Arthur, Bremen) := false\n", - " at(Sebastian, Basel) := false\n", - " at(Sebastian, Prague) := false\n", - " at(Sebastian, Trento) := false\n", - " at(Sebastian, Toulouse) := false\n", - " at(Sebastian, Bremen) := true\n", - " at(Gabi, Basel) := true\n", - " at(Gabi, Prague) := false\n", - " at(Gabi, Trento) := false\n", - " at(Gabi, Toulouse) := false\n", - " at(Gabi, Bremen) := false\n", - " at(Mum, Basel) := true\n", - " at(Mum, Prague) := false\n", - " at(Mum, Trento) := false\n", - " at(Mum, Toulouse) := false\n", - " at(Mum, Bremen) := false\n", - " travel_cost(Prague, Trento) := 49\n", - " travel_cost(Trento, Prague) := 49\n", - " travel_cost(Prague, Toulouse) := 53\n", - " travel_cost(Toulouse, Prague) := 53\n", - " travel_cost(Prague, Bremen) := 20\n", - " travel_cost(Bremen, Prague) := 20\n", - " travel_cost(Prague, Basel) := 78\n", - " travel_cost(Basel, Prague) := 78\n", - " travel_cost(Trento, Toulouse) := 89\n", - " travel_cost(Toulouse, Trento) := 89\n", - " travel_cost(Trento, Bremen) := 13\n", - " travel_cost(Bremen, Trento) := 13\n", - " travel_cost(Trento, Basel) := 9\n", - " travel_cost(Basel, Trento) := 9\n", - " travel_cost(Toulouse, Bremen) := 58\n", - " travel_cost(Bremen, Toulouse) := 58\n", - " travel_cost(Toulouse, Basel) := 8\n", - " travel_cost(Basel, Toulouse) := 8\n", - " travel_cost(Bremen, Basel) := 44\n", - " travel_cost(Basel, Bremen) := 44\n", - " destination(Gabi, Basel) := false\n", - " destination(Gabi, Prague) := true\n", - " destination(Gabi, Trento) := false\n", - " destination(Gabi, Toulouse) := false\n", - " destination(Gabi, Bremen) := false\n", - " destination(Andrea, Basel) := false\n", - " destination(Andrea, Prague) := true\n", - " destination(Andrea, Trento) := false\n", - " destination(Andrea, Toulouse) := false\n", - " destination(Andrea, Bremen) := false\n", - " destination(Arthur, Basel) := false\n", - " destination(Arthur, Prague) := true\n", - " destination(Arthur, Trento) := false\n", - " destination(Arthur, Toulouse) := false\n", - " destination(Arthur, Bremen) := false\n", - " destination(Sebastian, Basel) := false\n", - " destination(Sebastian, Prague) := true\n", - " destination(Sebastian, Trento) := false\n", - " destination(Sebastian, Toulouse) := false\n", - " destination(Sebastian, Bremen) := false\n", - " travel_cost(Basel, Basel) := 0\n", - " travel_cost(Prague, Prague) := 0\n", - " travel_cost(Trento, Trento) := 0\n", - " travel_cost(Toulouse, Toulouse) := 0\n", - " travel_cost(Bremen, Bremen) := 0\n", - "]\n", - "\n", - "goals = [\n", - " Forall (Attendee - Person a) Exists (Location destination_location) (at(a, destination_location) and destination(a, destination_location))\n", - "]\n", - "\n", - "quality metrics = [\n", - " minimize actions-cost: {'travel': travel_cost(l_from, l_to), 'default': None}\n", - "]\n" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "problem name = utfr_Travel to ICAPS\n", + "\n", + "types = [Person, Attendee - Person, Location]\n", + "\n", + "fluents = [\n", + " bool destination[person=Attendee - Person, location=Location]\n", + " bool at[p=Person, location=Location]\n", + " integer travel_cost[from_loc=Location, to_loc=Location]\n", + "]\n", + "\n", + "actions = [\n", + " action travel(Person p, Location l_from, Location l_to) {\n", + " preconditions = [\n", + " at(p, l_from)\n", + " ]\n", + " effects = [\n", + " if (l_to == Basel) then at(p, Basel) := true\n", + " if (not (l_to == Basel)) then at(p, Basel) := false\n", + " if (l_to == Prague) then at(p, Prague) := true\n", + " if (not (l_to == Prague)) then at(p, Prague) := false\n", + " if (l_to == Trento) then at(p, Trento) := true\n", + " if (not (l_to == Trento)) then at(p, Trento) := false\n", + " if (l_to == Toulouse) then at(p, Toulouse) := true\n", + " if (not (l_to == Toulouse)) then at(p, Toulouse) := false\n", + " if (l_to == Bremen) then at(p, Bremen) := true\n", + " if (not (l_to == Bremen)) then at(p, Bremen) := false\n", + " ]\n", + " }\n", + "]\n", + "\n", + "objects = [\n", + " Person: [Gabi, Mum, Andrea, Arthur, Sebastian]\n", + " Attendee - Person: [Gabi, Andrea, Arthur, Sebastian]\n", + " Location: [Basel, Prague, Trento, Toulouse, Bremen]\n", + "]\n", + "\n", + "initial fluents default = [\n", + "]\n", + "\n", + "initial values = [\n", + " at(Andrea, Basel) := false\n", + " at(Andrea, Prague) := false\n", + " at(Andrea, Trento) := true\n", + " at(Andrea, Toulouse) := false\n", + " at(Andrea, Bremen) := false\n", + " at(Arthur, Basel) := false\n", + " at(Arthur, Prague) := false\n", + " at(Arthur, Trento) := false\n", + " at(Arthur, Toulouse) := true\n", + " at(Arthur, Bremen) := false\n", + " at(Sebastian, Basel) := false\n", + " at(Sebastian, Prague) := false\n", + " at(Sebastian, Trento) := false\n", + " at(Sebastian, Toulouse) := false\n", + " at(Sebastian, Bremen) := true\n", + " at(Gabi, Basel) := true\n", + " at(Gabi, Prague) := false\n", + " at(Gabi, Trento) := false\n", + " at(Gabi, Toulouse) := false\n", + " at(Gabi, Bremen) := false\n", + " at(Mum, Basel) := true\n", + " at(Mum, Prague) := false\n", + " at(Mum, Trento) := false\n", + " at(Mum, Toulouse) := false\n", + " at(Mum, Bremen) := false\n", + " travel_cost(Prague, Trento) := 46\n", + " travel_cost(Trento, Prague) := 46\n", + " travel_cost(Prague, Toulouse) := 76\n", + " travel_cost(Toulouse, Prague) := 76\n", + " travel_cost(Prague, Bremen) := 81\n", + " travel_cost(Bremen, Prague) := 81\n", + " travel_cost(Prague, Basel) := 24\n", + " travel_cost(Basel, Prague) := 24\n", + " travel_cost(Trento, Toulouse) := 67\n", + " travel_cost(Toulouse, Trento) := 67\n", + " travel_cost(Trento, Bremen) := 12\n", + " travel_cost(Bremen, Trento) := 12\n", + " travel_cost(Trento, Basel) := 32\n", + " travel_cost(Basel, Trento) := 32\n", + " travel_cost(Toulouse, Bremen) := 59\n", + " travel_cost(Bremen, Toulouse) := 59\n", + " travel_cost(Toulouse, Basel) := 10\n", + " travel_cost(Basel, Toulouse) := 10\n", + " travel_cost(Bremen, Basel) := 94\n", + " travel_cost(Basel, Bremen) := 94\n", + " destination(Gabi, Basel) := false\n", + " destination(Gabi, Prague) := true\n", + " destination(Gabi, Trento) := false\n", + " destination(Gabi, Toulouse) := false\n", + " destination(Gabi, Bremen) := false\n", + " destination(Andrea, Basel) := false\n", + " destination(Andrea, Prague) := true\n", + " destination(Andrea, Trento) := false\n", + " destination(Andrea, Toulouse) := false\n", + " destination(Andrea, Bremen) := false\n", + " destination(Arthur, Basel) := false\n", + " destination(Arthur, Prague) := true\n", + " destination(Arthur, Trento) := false\n", + " destination(Arthur, Toulouse) := false\n", + " destination(Arthur, Bremen) := false\n", + " destination(Sebastian, Basel) := false\n", + " destination(Sebastian, Prague) := true\n", + " destination(Sebastian, Trento) := false\n", + " destination(Sebastian, Toulouse) := false\n", + " destination(Sebastian, Bremen) := false\n", + " travel_cost(Basel, Basel) := 0\n", + " travel_cost(Prague, Prague) := 0\n", + " travel_cost(Trento, Trento) := 0\n", + " travel_cost(Toulouse, Toulouse) := 0\n", + " travel_cost(Bremen, Bremen) := 0\n", + "]\n", + "\n", + "goals = [\n", + " Forall (Attendee - Person a) Exists (Location destination_location) (at(a, destination_location) and destination(a, destination_location))\n", + "]\n", + "\n", + "quality metrics = [\n", + " minimize actions-cost: {'travel': travel_cost(l_from, l_to), 'default': None}\n", + "]\n", + "\n", + "\n" + ] } ], "source": [ "utr_problem = utr_result.problem\n", - "utr_problem" + "print(utr_problem)" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 26, "metadata": { "slideshow": { "slide_type": "slide" @@ -1058,7 +1007,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "functools.partial(, map={action travel(Person p, Location l_from, Location l_to) {\n", + "functools.partial(, map={action travel(Person p, Location l_from, Location l_to) {\n", " preconditions = [\n", " at(p, l_from)\n", " ]\n", @@ -1103,7 +1052,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 27, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1116,7 +1065,7 @@ "text": [ "\u001b[96m\u001b[1mNOTE: To disable printing of planning engine credits, add this line to your code: `up.shortcuts.get_environment().credits_stream = None`\n", "\u001b[0m\u001b[96m *** Credits ***\n", - "\u001b[0m\u001b[96m * In operation mode `OneshotPlanner` at line 1 of `/tmp/ipykernel_133973/1887964203.py`, \u001b[0m\u001b[96myou are using the following planning engine:\n", + "\u001b[0m\u001b[96m * In operation mode `OneshotPlanner` at line 1 of `/tmp/ipykernel_173045/1887964203.py`, \u001b[0m\u001b[96myou are using the following planning engine:\n", "\u001b[0m\u001b[96m * Engine name: Fast Downward\n", " * Developers: Uni Basel team and contributors (cf. https://github.com/aibasel/downward/blob/main/README.md)\n", "\u001b[0m\u001b[96m * Description: \u001b[0m\u001b[96mFast Downward is a domain-independent classical planning system.\u001b[0m\u001b[96m\n", @@ -1132,7 +1081,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 28, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1140,18 +1089,15 @@ }, "outputs": [ { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "PlanGenerationResultStatus.SOLVED_SATISFICING\n" + ] } ], "source": [ - "res.status" + "print(res.status)" ] }, { @@ -1164,19 +1110,20 @@ }, "outputs": [ { - "data": { - "text/plain": [ - "SequentialPlan([travel(Sebastian, Bremen, Prague), travel(Gabi, Basel, Prague), travel(Arthur, Toulouse, Prague), travel(Andrea, Trento, Prague)])" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "SequentialPlan:\n", + " travel(Sebastian, Bremen, Prague)\n", + " travel(Gabi, Basel, Prague)\n", + " travel(Arthur, Toulouse, Prague)\n", + " travel(Andrea, Trento, Prague)\n" + ] } ], "source": [ "utr_plan = res.plan\n", - "utr_plan" + "print(utr_plan)" ] }, { @@ -1291,18 +1238,15 @@ }, "outputs": [ { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "PlanGenerationResultStatus.SOLVED_SATISFICING\n" + ] } ], "source": [ - "res.status" + "print(res.status)" ] }, { @@ -1330,7 +1274,7 @@ "output_type": "stream", "text": [ "\u001b[96m *** Credits ***\n", - "\u001b[0m\u001b[96m * In operation mode `OneshotPlanner` at line 3 of `/tmp/ipykernel_133973/1418646784.py`, \u001b[0m\u001b[96myou are using the following planning engine:\n", + "\u001b[0m\u001b[96m * In operation mode `OneshotPlanner` at line 3 of `/tmp/ipykernel_173045/1418646784.py`, \u001b[0m\u001b[96myou are using the following planning engine:\n", "\u001b[0m\u001b[96m * Engine name: SymK\n", " * Developers: David Speck (cf. https://github.com/speckdavid/symk/blob/master/README.md )\n", "\u001b[0m\u001b[96m * Description: \u001b[0m\u001b[96mSymK is a state-of-the-art domain-independent classical optimal and top-k planner.\u001b[0m\u001b[96m\n", @@ -1353,18 +1297,21 @@ "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "SequentialPlan([travel(Gabi, Basel, Trento), travel(Gabi, Trento, Bremen), travel(Andrea, Trento, Bremen), travel(Andrea, Bremen, Prague), travel(Gabi, Bremen, Prague), travel(Sebastian, Bremen, Prague), travel(Arthur, Toulouse, Basel), travel(Arthur, Basel, Trento), travel(Arthur, Trento, Bremen), travel(Arthur, Bremen, Prague)])" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "SequentialPlan:\n", + " travel(Sebastian, Bremen, Trento)\n", + " travel(Sebastian, Trento, Prague)\n", + " travel(Andrea, Trento, Prague)\n", + " travel(Gabi, Basel, Prague)\n", + " travel(Arthur, Toulouse, Basel)\n", + " travel(Arthur, Basel, Prague)\n" + ] } ], "source": [ - "res.plan" + "print(res.plan)" ] }, { @@ -1656,18 +1603,18 @@ "output_type": "stream", "text": [ "\u001b[96m *** Credits ***\n", - "\u001b[0m\u001b[96m * In operation mode `OneshotPlanner` at line 10 of `/tmp/ipykernel_133973/277381419.py`, \u001b[0m\u001b[96myou are using the following planning engine:\n", + "\u001b[0m\u001b[96m * In operation mode `OneshotPlanner` at line 10 of `/tmp/ipykernel_173045/277381419.py`, \u001b[0m\u001b[96myou are using the following planning engine:\n", "\u001b[0m\u001b[96m * Engine name: ENHSP\n", " * Developers: Enrico Scala\n", "\u001b[0m\u001b[96m * Description: \u001b[0m\u001b[96mExpressive Numeric Heuristic Search Planner.\u001b[0m\u001b[96m\n", "\u001b[0m\u001b[96m\n", "\u001b[0mstatus: SOLVED_SATISFICING\n", - "engine: SAT-enhsp\n", + "engine: enhsp\n", "plan: SequentialPlan:\n", - " travel(Gabi, Basel, Prague)\n", + " travel(Sebastian, Bremen, Prague)\n", " travel(Arthur, Toulouse, Prague)\n", " travel(Andrea, Trento, Prague)\n", - " travel(Sebastian, Bremen, Prague)\n" + " travel(Gabi, Basel, Prague)\n" ] } ], @@ -1707,7 +1654,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.6" + "version": "3.10.12" } }, "nbformat": 4, diff --git a/docs/notebooks/tutorial/planner-integration.ipynb b/docs/notebooks/tutorial/planner-integration.ipynb index 2b739e0f7..b9981091f 100644 --- a/docs/notebooks/tutorial/planner-integration.ipynb +++ b/docs/notebooks/tutorial/planner-integration.ipynb @@ -31,7 +31,7 @@ }, "outputs": [], "source": [ - "!pip install unified-planning" + "%pip install unified-planning" ] }, { From e1b0ad3e6a7add13222ae04af6b8f8b378f6b55f Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Thu, 9 Nov 2023 14:41:34 +0100 Subject: [PATCH 09/19] doc: add a very high-level presentation of planning --- docs/getting_started.rst | 3 ++- docs/getting_started/whats_planning.md | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 docs/getting_started/whats_planning.md diff --git a/docs/getting_started.rst b/docs/getting_started.rst index eadd8991d..24b2d65e8 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -5,7 +5,7 @@ Getting Started =============== -In this guide we present the main functionalities offered by the Unified-Planning library. A fully interactive guide is available on `Google Colab `_. +In this guide we present the main functionalities offered by the Unified-Planning library. .. toctree:: @@ -14,6 +14,7 @@ In this guide we present the main functionalities offered by the Unified-Plannin :hidden: :caption: Getting Started + getting_started/whats_planning getting_started/installation getting_started/quickstart notebooks/tutorial/modeling.ipynb diff --git a/docs/getting_started/whats_planning.md b/docs/getting_started/whats_planning.md new file mode 100644 index 000000000..7b4c7eaac --- /dev/null +++ b/docs/getting_started/whats_planning.md @@ -0,0 +1,22 @@ +# What is automated planning? + +**Automated Planning** is an area of *Artificial Intelligence* where the task is to choose and arrange actions in order to achieve some goal. + +This is a pretty definition, but also a very generic one! If you are not involved in this area, this probably means nothing to you! So, we tried to come up with a way to explain automated planning, that everyone might be able to understand, and even maybe repeat to their mothers. 😉 + +In a simple example, think of four-year-old Tom who needs to get dressed in the morning. Every morning, he wears his pyjamas, checks what clothes are available in his drawer and his mommy tells him how warm it will be that day. In a planning task, such information would be captured by the initial state. + + +> Tom’s goal (and the one of the planning tasks) is that he is suitably dressed, considering mom’s weather forecast. + +For each piece of clothing, there is an action to put it on. The obvious effect of the action is that afterwards, Tom wears this garment. But if you ever watched a young kid getting dressed, you know that it is not that simple. + +For example, one should wear a t-shirt before putting on the sweater. And one should not still wear the pyjamas when slipping into the t-shirt. In a planning task, such requirements would be formulated as preconditions of the actions. In the precondition of the corresponding action, we could also represent that winter boots are not an acceptable choice on a hot day (something Tom’s mom established by storing them away after repeated discussions). + +Most (non-fashionista) adults won’t consider getting dressed a problem they need to put much thought into. But companies face analogous tasks in very different domains – with a much larger space of possible actions and much more complex interrelations. + +It will take Tom several years (or at least months if you are a lucky parent!) to learn how to dress properly according to the weather and dressing preconditions. But imagine that you could teach this to your kid in a couple of days. Wouldn’t it be amazing? + +The same way in companies, their employees can take weeks or even years to develop a strategy that works well, and still, its quality is oftentimes far away from optimal. + +Automated planning offers a different approach to such problems: the user only describes the WHAT of the problem in a purely declarative fashion. The HOW is then resolved by the planning engine. \ No newline at end of file From 0ec5b07d5c27221e73d9522cdef7661ce63dc603 Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Thu, 9 Nov 2023 15:46:54 +0100 Subject: [PATCH 10/19] doc: add drone showcase --- docs/showcases.rst | 15 ++++++++++ docs/showcases/02-drones.md | 57 +++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 docs/showcases.rst create mode 100644 docs/showcases/02-drones.md diff --git a/docs/showcases.rst b/docs/showcases.rst new file mode 100644 index 000000000..3603ad820 --- /dev/null +++ b/docs/showcases.rst @@ -0,0 +1,15 @@ +Application Showcase +==================== + + +In this section, we showcase some of the applications and use-cases where the unified-planning library has been used. + +Our hope with this section is that you may find a use-case that you relate and learn from their experience. + +.. toctree:: + :maxdepth: 2 + :glob: + :hidden: + :caption: Applications Showcases + + showcases/* \ No newline at end of file diff --git a/docs/showcases/02-drones.md b/docs/showcases/02-drones.md new file mode 100644 index 000000000..5c36a3004 --- /dev/null +++ b/docs/showcases/02-drones.md @@ -0,0 +1,57 @@ +# Surveillance and Inspection Drones + +## Context + +This use case centers on the deployment of a decision-making system for the purpose of conducting drone surveys and inspections. It encompasses two distinct scenarios in which one or more drones, each equipped with cameras, are employed to survey an area, identify objects of interest, and closely inspect said objects, all within an energy-constrained context. Comprehensive information pertaining to this use case is available on the [AIOnDemand](https://www.ai4europe.eu/business-and-industry/case-studies/drones-surveillance-and-inspection) platform. + + +## Planning Problem Description + +The primary objective of this use case is to establish a system that facilitates the following functionalities: + +1. Surveying a specified area to pinpoint objects of interest. +2. Elaborating plans for conducting in-depth inspections of these objects of interest, including coordination among multiple available drones. +3. Facilitating the return of drones to the base for battery replacement in instances of low power, enabling the continuation of operations. + +To accomplish this, the use case factors in various metrics such as drone battery consumption, the distance between target objects to be inspected, the time required for inspecting a single target, and the number of drones employed for inspection. These metrics serve as the foundation for the model used in this use case. + + +## Modeling in UP + +The success of this use case depends on temporal planning for optimization, intertwined with numerical constraints. To address this complex scenario, numerical fluents are configured to internally assess constraints during both the planning and execution phases. + +For example, the critical consideration of drone battery consumption necessitates an emergent response. If the fluent pertaining to battery levels fails during execution, the drone initiates an emergency protocol involving a safe landing and subsequent battery replacement before recommencing its mission. Another instance involves the optimization of the distance between target objects following the initial survey. The problem is structured such that, after localizing the expected number of target objects, a specific rule is applied to establish the optimal order for inspecting these objects, thereby enhancing efficiency in terms of time consumption. + +The experiments conducted encompass two scenarios: one with a single drone and another involving two drones. The chosen planner for these experiments is Aries. These experiments take place in both simulated and real-world environments within a controlled setting. Incremental variations are incorporated to explore diverse approaches used to this use case, as outlined in the table below. + + +| Variation | Description | +|-----------|-------------| +| Sequential Planning | The problem is modeled with known locations for n targets requiring inspection. | +| Sequential Planning with replanning | The problem is modeled with known locations for n targets and executed in a simulation, with battery consumption interruptions prompting replanning based on the current state of the experiment. | +| Temporal Planning | The initial problem version is modeled with known target locations (n) and is executed with time constraints. Subsequently, a second version localizes the plates through survey actions, redefines the problem, extends the plan for the localized targets, and executes them. | +| Parallel Plan Generation | Building upon the previous variation, this experiment extends to parallel plan generation for scenarios involving multiple drones, each independently inspecting target objects. | + + +## Operation Modes and Integration Aspects + + +The model definition enables the system to generate plans that encompass surveying the area of interest, searching for specific objects (colored plates in this instance), object localization, and conducting detailed inspections of individual objects. The operation mode employed for this use case is One Shot Planning, and the Aries Planning Engine is used to devise optimal plans for the various experiments. + +The drones feature a Python interface that interfaces with the GenoM3 framework and its functional components, thereby gaining access to plan execution services. The Python interface, known as "py-genomix," and the plans generated using UP are directly mapped using a bridge functionality. This bridge facilitates the conversion of plans into executable services by means of dependency graphs. The integration of the bridge is achieved through the Embedded Systems Bridge (ESB), a TSB functionality that permits the integration of UP Services into framework-agnostic applications. + +## Lessons Learned + +Through the exploration of the various experimental stages and variations outlined above, we have learned many key lessons in the context of modeling and planning. A few of them are: + +1. **Efficient Model Definition for Expedited Planning**: Expressing a straightforward and simplified model definition can significantly speedup the planning process. By reducing the complexity of the model, planners can generate plans more swiftly. This allows for real-time or near-real-time decision-making. It has been essential to strike a balance between including essential details and minimizing unnecessary intricacies in the model. +2. **Critical Assessment of Model Definition**: Choices related to the implementation of instantaneous or durative actions can have a substantial impact on the time required to devise a solution. The tradeoff has been to balance the need for temporal accuracy of the problem with the computational cost when modeling the use case. +3. **Importance of Planning Engine Selection**: The selection of an appropriate planning engine has played an important role in addressing the complexity of the problem. The choice have been made based on the evaluation of the specific requirements, constraints, and other relevant factors. + +## Resources + +- [Experiment Landing Page](https://www.ai4europe.eu/business-and-industry/case-studies/drones-surveillance-and-inspection) +- A Closed-Loop Framework-Independent Bridge: from AIPlan4EU’s Unified Planning Platform to Embedded Systems. [Paper](https://icaps23.icaps-conference.org/program/workshops/planrob/PlanRob-23_paper_8.pdf) +- GenoM3 Templates: from Middleware Independence to Formal Models Synthesis. [Paper](https://arxiv.org/abs/1807.10154) +- Drone Experiment. [Github](https://github.com/lerema/genom3-experiment) +- Embedded Systems Bridge. [Github](https://github.com/aiplan4eu/embedded-systems-bridge) \ No newline at end of file From c6cf1f0d9002350bfba97e0e4d8f463f04130cd4 Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Thu, 9 Nov 2023 16:05:53 +0100 Subject: [PATCH 11/19] doc: add space showcase --- docs/index.rst | 1 + .../tutorial/planner-integration.ipynb | 6 +-- docs/showcases/01-space.md | 39 +++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 docs/showcases/01-space.md diff --git a/docs/index.rst b/docs/index.rst index 116dd1baa..8c5e25434 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -31,6 +31,7 @@ Welcome to Unified-Planning documentation! engines optimality examples + showcases notebooks/engines/README contributor_guide release_notes diff --git a/docs/notebooks/tutorial/planner-integration.ipynb b/docs/notebooks/tutorial/planner-integration.ipynb index b9981091f..75e0ed421 100644 --- a/docs/notebooks/tutorial/planner-integration.ipynb +++ b/docs/notebooks/tutorial/planner-integration.ipynb @@ -10,11 +10,11 @@ "\n", "In this simple demo we will create a new planning engine for the \"Oneshot\" operation mode, we will register the new planner in the UP library and we will test it on a simple problem.\n", "\n", - "[![Open In GitHub](https://img.shields.io/badge/see-Github-579aca?logo=github)](https://github.com/aiplan4eu/unified-planning/blob/master/docs/notebooks/planner-integration.ipynb)\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/aiplan4eu/unified-planning/blob/master/docs/notebooks/planner-integration.ipynb)\n", + "[![Open In GitHub](https://img.shields.io/badge/see-Github-579aca?logo=github)](https://github.com/aiplan4eu/unified-planning/blob/master/docs/notebooks/tutorial/planner-integration.ipynb)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/aiplan4eu/unified-planning/blob/master/docs/notebooks/tutorial/planner-integration.ipynb)\n", "\n", "**_NOTE:_** The unified planning library has auxiliary base classes for **integrating stand-alone PDDL planners** (not described in this notebook). They take care of writing the PDDL files, calling your planner and collecting the plan from disk on various operating systems.\n", - "For oneshot planning, use [`engines.PDDLPlanner`](https://github.com/aiplan4eu/unified-planning/blob/master/unified_planning/engines/pddl_planner.py). For anytime planning there is [`engines.PDDLAnytimePlanner`](https://github.com/aiplan4eu/unified-planning/blob/master/unified_planning/engines/pddl_anytime_planner.py).\n", + "For oneshot planning, use [*engines.PDDLPlanner*](https://github.com/aiplan4eu/unified-planning/blob/master/unified_planning/engines/pddl_planner.py). For anytime planning there is [*engines.PDDLAnytimePlanner*](https://github.com/aiplan4eu/unified-planning/blob/master/unified_planning/engines/pddl_anytime_planner.py).\n", "\n", "## Setup\n", "We start by installing the library with PIP" diff --git a/docs/showcases/01-space.md b/docs/showcases/01-space.md new file mode 100644 index 000000000..dc6afe80f --- /dev/null +++ b/docs/showcases/01-space.md @@ -0,0 +1,39 @@ +# Planetary Exploration + +## Context + +The ‘Planning for Space’ use-case targets the automation of the tactical planning process in the context of multi-asset human-robotic missions as prepared by the National Space Agencies, the European Commission and the European Space Agency. Typical examples are the ExoMars mission for Mars exploration and the Argonautes mission for moon exploration and exploitation. + +The objective of the Space domain TSB is to provide an end-to-end system for: + +- Producing automatically validated Partial Activity Plans from a set of goals provided by the operator, +- Producing Consolidated Activity Plans to be uploaded to the robotic system for execution. A Consolidated Activity Plan is defined as an aggregation of Partial Activity Plans proposed by geographically distributed teams of engineers and scientists participating in the mission, +- Integrate the resulting system into the ESA/TRASYS 3DROCS Space Robotics Ground Control Station (GCS), +- Validate the end-to-end system on real examples as foreseen in the ExoMars mission. + + +## Planning Problem Description + +The planning problem in question revolves around a rover designed for Mars exploration, serving as the central agent. This Mars rover exhibits a versatile range of capabilities, enabling it to engage in various activities and tasks essential for scientific research and exploration on the Martian surface. The primary objective is to construct a sequential plan that outlines a series of actions the rover must undertake to achieve a specific mission goal. The complexity of this planning task is compounded by the presence of mission timelines and deadlines, closely tied to specific communication windows with Earth, where the rover must transmit data and receive instructions. Furthermore, the rover must manage its limited resources, such as battery power and available data storage, adding an additional layer of complexity. The mission's overarching goal is framed as a partial plan, necessitating planning to transform it into a fully executable and successful mission on Mars. + +## Modeling in UP + +The modeling in the unified-planning library of this problem employed all the different types of fluents to accurately represent the rover's state and the status of its subsystems. To capture the state of these subsystems, boolean fluents and user-defined types were used, enabling a precise representation of the robot's condition. Additionally, numeric fluents were employed to represent and manage essential resources, such as battery power and storage space, within the rover. To address the unique nature of the mission's goal, extra fluents were introduced to capture the partial plans that needed to be completed for mission success. + +To provide a flexible and adaptable approach to planning, two planning models were constructed at different levels of abstraction. The more detailed model adopted a temporal planning approach, utilizing DurativeActions to account for the time required for various tasks and actions. This model was subsequently abstracted into a numerical planning model, where all actions were considered instantaneous (InstantaneousAction). This simplification was based on the assumption that, in the current system, robotic tasks could not be effectively parallelized. + +In an experimental iteration of the model, SimulatedEffects were explored as a means to represent resources that required external evaluation through a "Rehearsal-As-A-Service" tool. This allowed for a more dynamic representation of resource management within the planning framework, adapting to the complexities and uncertainties of Mars exploration. However, this version turned out to be too computationally expensive. + +## Operation Modes and Integration Aspects + +Several Operation Modes were used to facilitate different aspects of the planning and execution process. One such mode is `OneshotPlanning` which serves as the primary approach for generating plans. This mode allows the system to efficiently determine a feasible plan to accomplish specific mission objectives, ensuring that the rover's tasks and actions are executable and optimized. + +To further enhance the planning process, the `SequentialSimulator` mode was introduced. This mode was used in an approach where goals are serialized, and the simulator is invoked between calls to the Oneshot Planner. By simulating the outcome of each planning phase, the initial state is updated accordingly for the subsequent call to the planner. + +Moreover, the `PlanValidation` was exploited: this operation mode allows for the validation of manually defined plans, ensuring they meet predefined criteria and constraints. Users can leverage this mode to verify that their plans align with mission objectives and do not violate critical constraints, thereby enhancing the overall success and safety of the Mars rover mission. + +This software bridge was successfully integrated into an operational environment for supporting the tactical activity planning of rovers targeting robotic planetary exploration. The operation modes were easily wrapped in a service callable by 3DROCS using REST API. + +## Lessons Learned + +In solving this use case, we've learned that expressiveness in problem modeling comes at a cost and can slow down the planning process. Keeping things simple and straightforward, striking a balance between detail and simplicity, is a more effective strategy for finding solutions in shorter, more efficient timeframes. This approach has allowed us to enhance the adaptability of the planning system to the needs of the use case. From 6ba66331a6f8e753b1579753fd3dfc732218d238 Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Thu, 9 Nov 2023 17:00:33 +0100 Subject: [PATCH 12/19] doc: add picture for drone use case --- docs/showcases.rst | 13 +++++-------- docs/showcases/02-drones.md | 2 ++ docs/showcases/img/drone_laas.png | Bin 0 -> 57136 bytes 3 files changed, 7 insertions(+), 8 deletions(-) create mode 100644 docs/showcases/img/drone_laas.png diff --git a/docs/showcases.rst b/docs/showcases.rst index 3603ad820..8ed07a740 100644 --- a/docs/showcases.rst +++ b/docs/showcases.rst @@ -1,15 +1,12 @@ -Application Showcase -==================== +Applications Showcase +===================== -In this section, we showcase some of the applications and use-cases where the unified-planning library has been used. - -Our hope with this section is that you may find a use-case that you relate and learn from their experience. +In this section, we showcase some applications and use-cases where the unified-planning library has been used. +Our hope is that you may find a use-case that you can relate to and learn from their experience. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 :glob: - :hidden: - :caption: Applications Showcases showcases/* \ No newline at end of file diff --git a/docs/showcases/02-drones.md b/docs/showcases/02-drones.md index 5c36a3004..8199e3b88 100644 --- a/docs/showcases/02-drones.md +++ b/docs/showcases/02-drones.md @@ -4,6 +4,8 @@ This use case centers on the deployment of a decision-making system for the purpose of conducting drone surveys and inspections. It encompasses two distinct scenarios in which one or more drones, each equipped with cameras, are employed to survey an area, identify objects of interest, and closely inspect said objects, all within an energy-constrained context. Comprehensive information pertaining to this use case is available on the [AIOnDemand](https://www.ai4europe.eu/business-and-industry/case-studies/drones-surveillance-and-inspection) platform. +![Drone from LAAS](img/drone_laas.png) + ## Planning Problem Description diff --git a/docs/showcases/img/drone_laas.png b/docs/showcases/img/drone_laas.png new file mode 100644 index 0000000000000000000000000000000000000000..8d8c6c0d904cc93369ff1a4abd407798cc80550c GIT binary patch literal 57136 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 literal 0 HcmV?d00001 From 0549f50a050a534b14c17d402376cdb73b5cd5ae Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Thu, 9 Nov 2023 17:37:06 +0100 Subject: [PATCH 13/19] doc: improvements to problem representation --- docs/problem_representation.rst | 51 +++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/docs/problem_representation.rst b/docs/problem_representation.rst index 4249934b9..346f7112c 100644 --- a/docs/problem_representation.rst +++ b/docs/problem_representation.rst @@ -245,8 +245,8 @@ In all the examples below all the shortcuts must be imported, with the command: -Classical and Numeric Example ------------------------------ +Classical and Numeric Planning +------------------------------ The following example shows a simple robotic planning problem modeling a robot moving between locations while consuming battery. The example shows the basic functionalities and objects needed to declare the problem specification. A more detailed presentation of the different objects is available on the `Google Colab `_ Python notebook where we document and explain all the different classes and their semantics. .. literalinclude:: ./code_snippets/robot_battery.py @@ -256,8 +256,9 @@ The following example shows a simple robotic planning problem modeling a robot m In the current version, the Unified-Planning library allows the specification of classical, numerical and temporal planning problems. In order to support the latitude expressiveness levels we have operators for arithmetic such as plus minus times and division and specific temporal operators to attach conditions and effects to specific timings within the duration of an action. The library :ref:`documentation ` provides examples and describes the use of these functionalities. -Temporal Example ----------------- +Temporal Planning +----------------- + Temporal planning is the problem of finding a plan for a planning problem involving durative actions and/or temporal constraints. This means that it is possible to model actions having: @@ -269,19 +270,20 @@ This means that it is possible to model actions having: :lines: 3-44 -Scheduling Example ------------------- -Scheduling is a restricted form of temporal planning where the set of actions (usually called activities) are known in advance and the problem consists in deciding the timing and parameters of the activities. -Generally, scheduled problems involve resources and constraints that define the feasible space of solutions. -Since scheduling problems are very common and scheduling as a computer science problem belongs to a simpler complexity class (NP) with respect to temporal planning (PSPACE, under suitable assumptions) we created a dedicated representation for scheduling problems. -We can represent a generic scheduling problem using the SchedulingProblem class as shown in the example below. +Hierarchical Planning +--------------------- + +A *hierarchical planning* problem is a problem formulation which extends the Problem object described so far adding support for tasks, methods and decompositions. The general idea is that the planning problem is augmented with high-level tasks that represent abstract operations (e.g. processing an order, going to some distant location) that may require a combination of actions to be achieved. Each high-level task is associated with one or several methods that describe how the task can be decomposed into a set of lower-level tasks and actions.The presence of several methods for a single task represent alternative possibilities of achieving the same operation, among which the planner shall decide. The most important difference between hierarchical and non-hierarchical planning is that in hierarchical planning all actions of the plan must derive from a set of (partially ordered) high-level objective tasks, called the initial task network. +`[Detailed presentation 🔗] `__ + +.. literalinclude:: ./code_snippets/hierarchical_problem.py + :lines: 3-58 + :caption: Syntax Overview -.. literalinclude:: ./code_snippets/temporal_and_scheduling.py - :lines: 47-67 +Multi-Agent Planning +-------------------- -MultiAgent Example ------------------- Multi-agent Planning lifts planning to a context where many agents operate in a common environment, each with its own view of the domain. The objective is to produce a set of plans, one for each agent, which allows the agents to achieve their goals. The problem comes in several variants, depending on various features. Specifically, multi-agent planning can be competitive, meaning that the agents compete against each other in order to achieve their goal, or cooperative, which refers to the case where the agents collaborate towards a common goal. Another distinction is based on whether planning is performed in a centralized or distributed manner. In the former case, the planning responsibility is assigned to a single entity, which produces a plan consisting of actions, each to be delegated to some agent, while in the latter, the responsibility is distributed to the participating agents, each of which plans at a local level, by possibly exchanging information with the others; in this variant, each agent comes up with a local plan, and the execution of all plans must be appropriately coordinated at runtime. Finally, the setting may or may not require privacy-preservation, which refers to the requirement that every agent might decide not to disclose some information (consequently affecting the space of admissible solutions). @@ -289,17 +291,24 @@ The problem comes in several variants, depending on various features. Specifical :lines: 3-41 -Contingent Example ------------------- +Contingent Planning +------------------- A contingent planning problem represents an action-based problem in which the exact initial state is not entirely known and some of the actions produce “observations” upon execution. More specifically, some actions can be SensingActions, which indicate which fluents they observe and after the successful execution of such actions, the observed fluents become known to the executor. The inherent non-determinism in the initial state can therefore be “shrinked” by performing suitable SensingActions and a plan is then a strategy that prescribes what to execute based on the past observations. .. literalinclude:: ./code_snippets/multi_agent_and_contingent.py :lines: 44-95 -Hierarchical Example --------------------- -A hierarchical planning problem is a problem formulation which extends the Problem object described so far adding support for tasks, methods and decompositions. The general idea is that the planning problem is augmented with high-level tasks that represent abstract operations (e.g. processing an order, going to some distant location) that may require a combination of actions to be achieved. Each high-level task is associated with one or several methods that describe how the task can be decomposed into a set of lower-level tasks and actions.The presence of several methods for a single task represent alternative possibilities of achieving the same operation, among which the planner shall decide. The most important difference between hierarchical and non-hierarchical planning is that in hierarchical planning all actions of the plan must derive from a set of (partially ordered) high-level objective tasks, called the initial task network. +Scheduling +---------- + +Scheduling is a restricted form of temporal planning where the set of actions (usually called activities) are known in advance and the problem consists in deciding the timing and parameters of the activities. +Generally, scheduled problems involve resources and constraints that define the feasible space of solutions. +Since scheduling problems are very common and scheduling as a computer science problem belongs to a simpler complexity class (NP) with respect to temporal planning (PSPACE, under suitable assumptions) we created a dedicated representation for scheduling problems. +We can represent a generic scheduling problem using the SchedulingProblem class as shown in the example below. +`[Detailed presentation 🔗] `__ + +.. literalinclude:: ./code_snippets/temporal_and_scheduling.py + :lines: 47-67 + :caption: Syntax Overview -.. literalinclude:: ./code_snippets/hierarchical_problem.py - :lines: 3-58 From 74db699922abf283247be19baf4fbf40cc31b8bb Mon Sep 17 00:00:00 2001 From: Luca Framba Date: Wed, 15 Nov 2023 10:31:18 +0100 Subject: [PATCH 14/19] Doc(04_testing_engine.rst): Populated the docs/engines/04_testing_engine.rst file with the instruction on how to use the report.py script --- docs/engines/04_testing_engine.rst | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/engines/04_testing_engine.rst b/docs/engines/04_testing_engine.rst index 5b6cbe748..b030b25b8 100644 --- a/docs/engines/04_testing_engine.rst +++ b/docs/engines/04_testing_engine.rst @@ -1,4 +1,30 @@ Testing an Engine Integration ============================= -TODO: how to run report, ... (maybe merge with "integrating") \ No newline at end of file +The ``report.py`` script in ``unified-planning/up_test_cases`` is used to test an ``Engine`` on a collection of ``Problems``. + + +Report.py usage +--------------- + +To get a parameters description of the ``report.py`` script run ``python3 up_test_cases/report.py -h``; this gives an idea of the parameters and their usage. + +To test an engine that is not a default engine, follow this procedure: `Engine selection and preference list `__ + +Some examples: + + +* ``python3 up_test_cases/report.py aries fast-downward -m oneshot``: runs the ``OneshotPlanner`` mode of ``aries`` and ``fast-downward`` on all the problems. +* ``python3 up_test_cases/report.py tamer -m validation -f numeric temporal``: runs the ``Validator`` mode of ``tamer`` on all the problems that contain the word ""numeric" or "temporal". +* ``python3 up_test_cases/report.py lpg -e performance -t 30``: runs ``lpg`` on all the default problems and the problems in the package "performance", with a timeout of 30 seconds. +* ``python3 up_test_cases/report.py enhsp -p builtin.numeric performance``: runs ``enhsp`` on problems defined in the packages ""numeric" and "performance". + + +Add custom problems +------------------- + +To add custom problems you need to create a python package that exposes a method called ``get_test_cases``. This method returns a ``Dict[str, TestCase]`` (where ``TestCase`` is defined in the ``unified_planning.test.__init__.py`` file). + +After you have a package like this that is available in the python path (``import example_package`` must not fail), you can specify ``example_package`` in the options ``-p (--packages)`` or ``-e (--extra-packages)`` of the ``report.py`` script. + +This should allow the user to specify it's own set of problems to submit to the engine testing. From 992620b5642ca0ab3714be950bc08512f16d98c7 Mon Sep 17 00:00:00 2001 From: Alessandro Valentini Date: Thu, 16 Nov 2023 15:03:00 +0100 Subject: [PATCH 15/19] Updated the space use case document --- docs/showcases/01-space.md | 8 +++++++- docs/showcases/img/rover_exomars.jpg | Bin 0 -> 13076 bytes 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 docs/showcases/img/rover_exomars.jpg diff --git a/docs/showcases/01-space.md b/docs/showcases/01-space.md index dc6afe80f..a4e09c8dc 100644 --- a/docs/showcases/01-space.md +++ b/docs/showcases/01-space.md @@ -2,7 +2,7 @@ ## Context -The ‘Planning for Space’ use-case targets the automation of the tactical planning process in the context of multi-asset human-robotic missions as prepared by the National Space Agencies, the European Commission and the European Space Agency. Typical examples are the ExoMars mission for Mars exploration and the Argonautes mission for moon exploration and exploitation. +The ‘Planning for Space’ use-case targets the automation of the tactical planning process in the context of multi-asset human-robotic missions as prepared by the National Space Agencies, the European Commission and the European Space Agency. Typical examples are the [ExoMars](https://www.esa.int/Science_Exploration/Human_and_Robotic_Exploration/Exploration/ExoMars/ExoMars_rover) mission for Mars exploration and the Argonautes mission for moon exploration and exploitation. The objective of the Space domain TSB is to provide an end-to-end system for: @@ -11,6 +11,7 @@ The objective of the Space domain TSB is to provide an end-to-end system for: - Integrate the resulting system into the ESA/TRASYS 3DROCS Space Robotics Ground Control Station (GCS), - Validate the end-to-end system on real examples as foreseen in the ExoMars mission. +![ExoMars rover](img/rover_exomars.jpg) ## Planning Problem Description @@ -37,3 +38,8 @@ This software bridge was successfully integrated into an operational environment ## Lessons Learned In solving this use case, we've learned that expressiveness in problem modeling comes at a cost and can slow down the planning process. Keeping things simple and straightforward, striking a balance between detail and simplicity, is a more effective strategy for finding solutions in shorter, more efficient timeframes. This approach has allowed us to enhance the adaptability of the planning system to the needs of the use case. + +## Resources + +- [Planning for Space page](https://www.ai4europe.eu/business-and-industry/case-studies/planning-space) +- TSB for the Space Use Case. [Github](https://github.com/aiplan4eu/tsb-space) diff --git a/docs/showcases/img/rover_exomars.jpg b/docs/showcases/img/rover_exomars.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ec71058bfcbabe9b3b95a642245a35d21cd6e2a3 GIT binary patch literal 13076 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 w7IyE Date: Thu, 16 Nov 2023 16:19:24 +0100 Subject: [PATCH 16/19] Added info on the requirements to run the report.py file --- docs/engines/04_testing_engine.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/engines/04_testing_engine.rst b/docs/engines/04_testing_engine.rst index b030b25b8..b7877b9fb 100644 --- a/docs/engines/04_testing_engine.rst +++ b/docs/engines/04_testing_engine.rst @@ -3,6 +3,16 @@ Testing an Engine Integration The ``report.py`` script in ``unified-planning/up_test_cases`` is used to test an ``Engine`` on a collection of ``Problems``. +Requirements to run the script +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To run the script you need to clone the unified-planning repo, get in the unified-planning folder and install the wanted engines, like so:: + + git clone https://github.com/aiplan4eu/unified-planning.git + cd unified-planning + pip3 install ./[fast-downward,aries] # to test fast-downward and aries, for example_package + python3 up_test_cases/report.py # run the report script + Report.py usage --------------- From b9a3179422df6219c1a3e1eb6b59b17bd2bd4cb6 Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Fri, 17 Nov 2023 14:19:50 +0100 Subject: [PATCH 17/19] doc: Minor fixes + some details on available engines --- docs/engines/01_available_engines.rst | 53 +++++++++++++++------------ docs/engines/04_testing_engine.rst | 4 +- docs/problem_representation.rst | 2 +- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/docs/engines/01_available_engines.rst b/docs/engines/01_available_engines.rst index a96fac7cc..0aec00955 100644 --- a/docs/engines/01_available_engines.rst +++ b/docs/engines/01_available_engines.rst @@ -6,6 +6,11 @@ Available Engines The tables below give a high-level overview of the planning engines integrated with the UP. +The characterization of the planning systems is deliberately kept very broad and is mainly intended to help identify which planners are relevant for a given class of problem. We redirect you to the specific documentation of each planner for a more in depth characterization of their features and limitations. + +In this page only appear solvers that are *officially integrated* in the unified planning library, for which we have reasonable confidence that they will not give you incorrect results (i.e. they successfully pass all :ref:`engine tests `), now and in the future (i.e. they have a clearly identified maintainer). +We do mention some solvers whose integration is partial, but that may be nevertheless of interest in some special cases. + Action-Based Planning ^^^^^^^^^^^^^^^^^^^^^ @@ -19,31 +24,31 @@ Action-Based Planning - Metrics * - `Fast-Downward`_ - OneShot, Anytime - - 🗸 + - Y - - - plan length, action costs * - `ENHSP`_ - OneShot, Anytime - - 🗸 - - 🗸 + - Y + - Y - - action costs, final value * - `Tamer`_ - OneShot - - 🗸 - - 🗸 - - 🗸 + - Y + - Y + - Y - * - `Aries`_ [#aries-actions]_ - OneShot, Anytime - - 🗸 - - 🗸 (integers) - - 🗸 + - Y + - Y (integers) + - Y - plan length, makespan, action costs * - `Pyperplan`_ [#pyperplan-note]_ - OneShot - - 🗸 + - Y - - - @@ -64,23 +69,23 @@ Plan Validation - Hierarchical - Scheduling * - `UP (builtin)` - - 🗸 - - + - Y + - Y - - - * - `Tamer`_ - - 🗸 - - 🗸 - - 🗸 + - Y + - Y + - Y - - * - `Aries`_ - - 🗸 - - 🗸 - - 🗸 - - 🗸 - - 🗸 + - Y + - Y + - Y + - Y + - Y Hierarchical Planning @@ -97,10 +102,10 @@ Hierarchical Planning - Metrics * - `Aries`_ - OneShot, Anytime - - 🗸 - - 🗸 (integers) - - 🗸 - - 🗸 + - Y + - Y (integers) + - Y + - Y - plan length, makespan, action costs A WIP integration is known for `SIADEX `_. diff --git a/docs/engines/04_testing_engine.rst b/docs/engines/04_testing_engine.rst index b7877b9fb..fcb7a3ef7 100644 --- a/docs/engines/04_testing_engine.rst +++ b/docs/engines/04_testing_engine.rst @@ -1,3 +1,5 @@ +.. _testing_engines: + Testing an Engine Integration ============================= @@ -6,7 +8,7 @@ The ``report.py`` script in ``unified-planning/up_test_cases`` is used to test a Requirements to run the script ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To run the script you need to clone the unified-planning repo, get in the unified-planning folder and install the wanted engines, like so:: +To run the script you need to clone the unified-planning repo, get in the ``unified-planning`` folder and install the wanted engines, like so:: git clone https://github.com/aiplan4eu/unified-planning.git cd unified-planning diff --git a/docs/problem_representation.rst b/docs/problem_representation.rst index 346f7112c..f97d265bb 100644 --- a/docs/problem_representation.rst +++ b/docs/problem_representation.rst @@ -1,7 +1,7 @@ Problem Representation ====================== -The main functionality offered by the library concerns the specification of a planning problem. In particular, the UP library supports the following classes of planning problems: Classical, Numeric, Temporal, Scheduling, Multi-Agent, Hierarchical, Task and Motion Planning (TAMP) and Conformant. For each of these, we provide a simple description and a reference code at the end of the section. +The main functionality offered by the library concerns the specification of a planning problem. In particular, the UP library supports the following classes of planning problems: Classical, Numeric, Temporal, Scheduling, Multi-Agent, Hierarchical, Task and Motion Planning (TAMP) and Conformant. For each of these, we provide a short description, a syntax overview and a link to a detailed discussion. One of the key element of the problem specifications is the ProblemKind class (automatically computed by all the planning problems classes via the kind property), which is a collection of flags, documented in the table below, that identifies the modeling features used in any problem specification, so that the library can determine the applicability of each engine for a certain query. From 88481befb192e6f7eb4938c2a70f2d9154d91861 Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Fri, 17 Nov 2023 14:20:58 +0100 Subject: [PATCH 18/19] doc: Remove import of planning engines' READMEs --- docs/conf.py | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a3867d83d..859b675f2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -336,37 +336,5 @@ autodoc_member_order = "bysource" -# # -- Generating Planning Engine Scripts ------------------------------------------- - -# engines = { -# "aries": "https://raw.githubusercontent.com/plaans/aries/master/planning/unified/plugin/README.md", -# "tamer": "https://raw.githubusercontent.com/aiplan4eu/up-tamer/master/README.md", -# "enhsp": "https://raw.githubusercontent.com/aiplan4eu/up-enhsp/master/README.md", -# "spiderplan": "https://raw.githubusercontent.com/aiplan4eu/up-spiderplan/master/README.md", -# "fmap": "https://raw.githubusercontent.com/aiplan4eu/up-fmap/master/README.md", -# "lpg": "https://raw.githubusercontent.com/aiplan4eu/up-lpg/master/README.md", -# "pyperplan": "https://raw.githubusercontent.com/aiplan4eu/up-pyperplan/master/README.md", -# "fast_downward": "https://raw.githubusercontent.com/aiplan4eu/up-fast-downward/main/README.md", -# } - -# SKIP_ENGINES = [] -# for skipped in SKIP_ENGINES: -# engines.popitem(skipped) -# engines = dict(sorted(engines.items())) -# ENGINES_DIR = os.path.join(os.path.dirname(__file__), "engines") - -# if not os.path.exists(ENGINES_DIR): -# os.makedirs(ENGINES_DIR) - -# for i, (name, source) in enumerate(engines.items()): -# with open(f"{ENGINES_DIR}/{i+1}-{name}.md", "w") as f: -# response = requests.get(source) -# if response.status_code == 200: -# f.write(response.text) -# else: -# Warning(f"Error getting source for planning engine {name}") - - # Create API reference doc - generate_api_doc.generate() From faf34e8e30eb903cef5b5af47afc2276512ff39d Mon Sep 17 00:00:00 2001 From: Arthur Bit-Monnot Date: Fri, 17 Nov 2023 14:26:11 +0100 Subject: [PATCH 19/19] doc: Remove planning-engines-examples page from top level --- docs/conf.py | 9 ++++++++- docs/index.rst | 1 - 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 859b675f2..1c2a67d3d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -101,7 +101,14 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "README.md", "readme.rst"] +exclude_patterns = [ + "_build", + "Thumbs.db", + ".DS_Store", + "README.md", + "readme.rst", + "notebooks/engines/README.md", +] # The reST default role (used for this markup: `text`) to use for all # documents. diff --git a/docs/index.rst b/docs/index.rst index 8c5e25434..dd9dd1eff 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -32,7 +32,6 @@ Welcome to Unified-Planning documentation! optimality examples showcases - notebooks/engines/README contributor_guide release_notes contributions