Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various improvements to documentation #517

Merged
merged 19 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/*

61 changes: 32 additions & 29 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
arbimo marked this conversation as resolved.
Show resolved Hide resolved


# 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
124 changes: 124 additions & 0 deletions docs/engines/01_available_engines.rst
Original file line number Diff line number Diff line change
@@ -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
- 🗸
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I build the docs locally, this symbol is not rendered well. Am I missing any requirements?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange, it is just a UTF-8 character. Probably, a font problem. I'll change it to Y for better compatibility (but it looks less cool)

-
-
- 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)`
- 🗸
-
arbimo marked this conversation as resolved.
Show resolved Hide resolved
-
-
-
* - `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 <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
Loading