Skip to content

Commit

Permalink
Merge pull request #517 from aiplan4eu/docs
Browse files Browse the repository at this point in the history
Various improvements to documentation
  • Loading branch information
alvalentini committed Nov 17, 2023
2 parents a6dacbd + faf34e8 commit 73cacaa
Show file tree
Hide file tree
Showing 34 changed files with 2,236 additions and 169 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion docs/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
engines/*

40 changes: 9 additions & 31 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -336,34 +343,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()
40 changes: 40 additions & 0 deletions docs/contributor_guide.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
Contributor's Guide
===================



Documentation
-------------

The documentation is maintained in the `docs/` folder of the `main repository <https://github.com/aiplan4eu/unified-planning/tree/master/docs>`_
It consists of a collection of reStructuredText documents and python notebooks that rendered to HTML and published on `readthedocs <https://unified-planning.readthedocs.io/en/latest/>`_ 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 <https://github.com/aiplan4eu/unified-planning/blob/master/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
74 changes: 9 additions & 65 deletions docs/engines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,13 @@ 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-name> <engine-name> <engine-name>
[engine <engine-name>]
module_name: <module-name>
class_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:

engines/*
:maxdepth: 2
:glob:
:hidden:
:caption: Getting Started

engines/01_available_engines.rst
engines/02_engine_selection.rst
notebooks/tutorial/planner-integration.ipynb
engines/04_testing_engine.rst
129 changes: 129 additions & 0 deletions docs/engines/01_available_engines.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@

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 <testing_engines>`), 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
^^^^^^^^^^^^^^^^^^^^^

.. list-table::

* - Engine
- Operation modes
- Classical
- Numeric
- Temporal
- 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
- Y
- Y (integers)
- Y
- plan length, makespan, action costs
* - `Pyperplan`_ [#pyperplan-note]_
- OneShot
- Y
-
-
-

.. [#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)`
- Y
- Y
-
-
-
* - `Tamer`_
- Y
- Y
- Y
-
-
* - `Aries`_
- Y
- Y
- Y
- Y
- Y


Hierarchical Planning
^^^^^^^^^^^^^^^^^^^^^

.. list-table::

* - Engine
- Operation modes
- Total-Order
- Partial-Order
- Numeric
- Temporal
- Metrics
* - `Aries`_
- OneShot, Anytime
- Y
- Y (integers)
- Y
- Y
- plan length, makespan, action costs

A WIP integration is known for `SIADEX <https://github.com/UGR-IntelligentSystemsGroup/up-siadex/>`_.

Scheduling
^^^^^^^^^^

The only planner with full support for scheduling is `Aries`_. Integration work is known for the `discrete-optimization suite <https://github.com/aiplan4eu/up-discreteoptimization>`_ and for `PPS <https://github.com/aiplan4eu/up-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
63 changes: 63 additions & 0 deletions docs/engines/02_engine_selection.rst
Original file line number Diff line number Diff line change
@@ -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-name> <engine-name> <engine-name>
[engine <engine-name>]
module_name: <module-name>
class_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.



Loading

0 comments on commit 73cacaa

Please sign in to comment.