{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "6nOTljC_mTMn" }, "source": [ "## Basic Example\n", "\n", "This python notebook shows the basic usage of the unified planning library.\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/01-basic-example.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/01-basic-example.ipynb)\n", "\n", "\n", "In particular we will go through the following steps:\n", "\n", " - create a classical planning problem;\n", " - call a planner to solve the problem;\n", " - go beyond plan generation showing how to validate a plan and how to ground a planning problem;\n", " - call multiple planners in parallel." ] }, { "cell_type": "markdown", "metadata": { "id": "t8dCcpf7mivV" }, "source": [ "### Setup\n" ] }, { "cell_type": "markdown", "metadata": { "id": "CwlvEzKrm1jT" }, "source": [ "First, we install unified_planning library and its dependencies from PyPi. Here, we use the `--pre` flag to use the latest development build." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "id": "BoqALxJWdfl8", "tags": [ "remove_from_CI" ] }, "outputs": [], "source": [ "!apt install graphviz graphviz-dev\n", "%pip install unified-planning[pyperplan,tamer,plot]" ] }, { "cell_type": "markdown", "metadata": { "id": "iNHFHxQKnKIp" }, "source": [ "We are now ready to use the Unified-Planning library!" ] }, { "cell_type": "markdown", "metadata": { "id": "9dP5scv7nNJu" }, "source": [ "### Imports\n", "The basic imports we need for this demo are abstracted in the `shortcuts` package." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "id": "06rETnGAfQHg" }, "outputs": [], "source": [ "import unified_planning\n", "from unified_planning.shortcuts import *" ] }, { "cell_type": "markdown", "metadata": { "id": "i8J7rP0cnvXq" }, "source": [ "### Problem representation\n", "\n", "In this example, we will model a very simple robot navigation problem.\n", "\n", "#### Types\n", "\n", "The first thing to do is to introduce a \"UserType\" to model the concept of a location. It is possible to introduce as many types as needed; then, for each type we will define a set of objects of that type. \n", "\n", "In addition to `UserType`s we have three built-in types: `Bool`, `Real` and `Integer`. " ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "id": "huAy2IbVn0GZ" }, "outputs": [], "source": [ "Location = UserType('Location')" ] }, { "cell_type": "markdown", "metadata": { "id": "fDukLfPPn20t" }, "source": [ "#### Fluents and constants\n", "\n", "The basic variables of a planning problem are called \"fluents\" and are quantities that can change over time. Fluents can have differen types, in this first example we will stick to classical \"predicates\" that are fluents of boolean type. Moreover, fluents can have parameters: effectively describing multiple variables.\n", "\n", "For example, a booean fluent `connected` with two parameters of type `Location` (that can be interpreted as `from` and `to`) can be used to model a graph of locations: there exists an edge between two locations `a` and `b` if `connected(a, b)` is true.\n", "\n", "In this example, `connected` will be a constant (i.e. it will never change in any execution), but another fluent `robot_at` will be used to model where the robot is: the robot is in locatiopn `l` if and only if `robot_at(l)` is true (we will ensure that exactly one such `l` exists, so that the robot is always in one single location)." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "id": "LZUgad7ZoA2p" }, "outputs": [], "source": [ "robot_at = unified_planning.model.Fluent('robot_at', BoolType(), l=Location)\n", "connected = unified_planning.model.Fluent('connected', BoolType(), l_from=Location, l_to=Location)" ] }, { "cell_type": "markdown", "metadata": { "id": "rVzqSj3XoDPa" }, "source": [ "#### Actions\n", "\n", "Now we have the problem variables, but in order to describe the possible evolutions of a systems we need to describe how these variables can be changed and how they can evolve. We model this problem using classical, action-based planning, where a set of actions is used to characterize the possible transitions of the system from a state to another.\n", "\n", "An action is a transition that can be applied if a specified set of preconditions is satisfied and that prescribes a set of effects that change the value of some fluents. All the fluents that are subjected to the action effects are unchanged.\n", "\n", "We allow _lifted_ actions, that are action with parameters: the parameters can be used to specify preconditions or effects and the planner will select among the possible values of each parameters the ones to be used to characterize a specific action. \n", "\n", "In our example, we introduce an action called `move` that has two parameters of type `Location` indicating the current position of the robot `l_from` and the intended destination of the movement `l_to`. The `move(a, b)` action is applicable only when the robot is in position `a` (i.e. `robot_at(a)`) and if `a` and `b` are connected locations (i.e. `connected(a, b)`). As a result of applying the action `move(a, b)`, the robot is no longer in `a` and is instead in location `b`.\n", "\n", "In the unified_planning, we can create actions by instantiating the `unified_planning.InstantaneousAction` class; parameters are specified as keyword arguments to the constructor as shown below. Preconditions and effects are added by means of the `add_precondition` and `add_effect` methods. " ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "id": "dRfrnEOfoHD8" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "action move(Location l_from, Location l_to) {\n", " preconditions = [\n", " connected(l_from, l_to)\n", " robot_at(l_from)\n", " ]\n", " effects = [\n", " robot_at(l_from) := false\n", " robot_at(l_to) := true\n", " ]\n", " }\n" ] } ], "source": [ "move = unified_planning.model.InstantaneousAction('move', l_from=Location, l_to=Location)\n", "l_from = move.parameter('l_from')\n", "l_to = move.parameter('l_to')\n", "move.add_precondition(connected(l_from, l_to))\n", "move.add_precondition(robot_at(l_from))\n", "move.add_effect(robot_at(l_from), False)\n", "move.add_effect(robot_at(l_to), True)\n", "print(move)" ] }, { "cell_type": "markdown", "metadata": { "id": "iMuggWWioJ8K" }, "source": [ "#### Creating the problem\n", "\n", "The class that represents a planning problem is `unified_planning.Problem`, it contains the set of fluents, the actions, the objects, an intial value for all the fluents and a goal to be reached by the planner. We start by adding the entities we created so far. Note that entities are not bound to one problem, we can create the actions and fluents one and create multiple problems with them." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "id": "pgrJOj6ioMSC" }, "outputs": [], "source": [ "problem = unified_planning.model.Problem('robot')\n", "problem.add_fluent(robot_at, default_initial_value=False)\n", "problem.add_fluent(connected, default_initial_value=False)\n", "problem.add_action(move)" ] }, { "cell_type": "markdown", "metadata": { "id": "35A3dp--oOOS" }, "source": [ "The set of objects is a set of `unified_planning.Object` instances, each represnting an element of the domain. In this example, we create `NLOC` (set to 10) locations named `l0` to `l9`. We can create the set of objects and add it to the problem as follows." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "id": "jbwJbJv8oQ9B" }, "outputs": [], "source": [ "NLOC = 10\n", "locations = [unified_planning.model.Object('l%s' % i, Location) for i in range(NLOC)]\n", "problem.add_objects(locations)" ] }, { "cell_type": "markdown", "metadata": { "id": "L-MnST4ioTKo" }, "source": [ "Then, we need to specify the initial state. We used the `default_initial_value` specification when adding the fluents, so it suffices to indicate the fluents that are initially true (this is called \"closed-world assumption\". Without this specification, we would need to initialize all the possible instantiation of all the fluents).\n", "\n", "In this example, we connect location `li` with location `li+1`, creating a simple \"linear\" graph lof locations and we set the initial position of the robot in location `l0`." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "id": "t7jLGJ1xoVxq" }, "outputs": [], "source": [ "problem.set_initial_value(robot_at(locations[0]), True)\n", "for i in range(NLOC - 1):\n", " problem.set_initial_value(connected(locations[i], locations[i+1]), True)" ] }, { "cell_type": "markdown", "metadata": { "id": "re1sYZHKoYx5" }, "source": [ "Finally, we set the goal of the problem. In this example, we set ourselves to reach location `l9`." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "id": "4zKqcGHlocdY" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "problem name = robot\n", "\n", "types = [Location]\n", "\n", "fluents = [\n", " bool robot_at[l=Location]\n", " bool connected[l_from=Location, l_to=Location]\n", "]\n", "\n", "actions = [\n", " action move(Location l_from, Location l_to) {\n", " preconditions = [\n", " connected(l_from, l_to)\n", " robot_at(l_from)\n", " ]\n", " effects = [\n", " robot_at(l_from) := false\n", " robot_at(l_to) := true\n", " ]\n", " }\n", "]\n", "\n", "objects = [\n", " Location: [l0, l1, l2, l3, l4, l5, l6, l7, l8, l9]\n", "]\n", "\n", "initial fluents default = [\n", " bool robot_at[l=Location] := false\n", " bool connected[l_from=Location, l_to=Location] := false\n", "]\n", "\n", "initial values = [\n", " robot_at(l0) := true\n", " connected(l0, l1) := true\n", " connected(l1, l2) := true\n", " connected(l2, l3) := true\n", " connected(l3, l4) := true\n", " connected(l4, l5) := true\n", " connected(l5, l6) := true\n", " connected(l6, l7) := true\n", " connected(l7, l8) := true\n", " connected(l8, l9) := true\n", "]\n", "\n", "goals = [\n", " robot_at(l9)\n", "]\n", "\n", "\n" ] } ], "source": [ "problem.add_goal(robot_at(locations[-1]))\n", "print(problem)" ] }, { "cell_type": "markdown", "metadata": { "id": "OTDDF5M1oezl" }, "source": [ "### Solving Planning Problems\n", "\n", "The most direct way to solve a planning problem is to select an available planning engine by name and use it to solve the problem. In the following we use `pyperplan` to solve the problem and print the plan." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "id": "8FTO4AoTojko" }, "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_154533/1237117673.py`, \u001b[0m\u001b[96myou are using the following planning engine:\n", "\u001b[0m\u001b[96m * Engine name: pyperplan\n", " * Developers: Albert-Ludwigs-Universität Freiburg (Yusra Alkhazraji, Matthias Frorath, Markus Grützner, Malte Helmert, Thomas Liebetraut, Robert Mattmüller, Manuela Ortlieb, Jendrik Seipp, Tobias Springenberg, Philip Stahl, Jan Wülfing)\n", "\u001b[0m\u001b[96m * Description: \u001b[0m\u001b[96mPyperplan is a lightweight STRIPS planner written in Python.\u001b[0m\u001b[96m\n", "\u001b[0m\u001b[96m\n", "\u001b[0mPyperplan returned: SequentialPlan:\n", " move(l0, l1)\n", " move(l1, l2)\n", " move(l2, l3)\n", " move(l3, l4)\n", " move(l4, l5)\n", " move(l5, l6)\n", " move(l6, l7)\n", " move(l7, l8)\n", " move(l8, l9)\n" ] } ], "source": [ "with OneshotPlanner(name='pyperplan') as planner:\n", " result = planner.solve(problem)\n", " if result.status == up.engines.PlanGenerationResultStatus.SOLVED_SATISFICING:\n", " print(\"Pyperplan returned: %s\" % result.plan)\n", " else:\n", " print(\"No plan found.\")" ] }, { "cell_type": "markdown", "metadata": { "id": "Q-Pju4K2q_bM" }, "source": [ "The unified_planning can also automatically select, among the available planners installed on the system, one that is expressive enough for the problem at hand." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "id": "wuTcp_xTxvTj" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[96m *** Credits ***\n", "\u001b[0m\u001b[96m * In operation mode `OneshotPlanner` at line 1 of `/tmp/ipykernel_154533/3190604266.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[0mFast Downward returned: SequentialPlan:\n", " move(l0, l1)\n", " move(l1, l2)\n", " move(l2, l3)\n", " move(l3, l4)\n", " move(l4, l5)\n", " move(l5, l6)\n", " move(l6, l7)\n", " move(l7, l8)\n", " move(l8, l9)\n" ] } ], "source": [ "with OneshotPlanner(problem_kind=problem.kind) as planner:\n", " result = planner.solve(problem)\n", " print(\"%s returned: %s\" % (planner.name, result.plan))" ] }, { "cell_type": "markdown", "metadata": { "id": "6KEe1f_Zx71o" }, "source": [ "In this example, Pyperplan was selected. The `problem.kind` property, returns an object that describes the characteristics of the problem." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "id": "Zmz6B_CcyABQ" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'FLAT_TYPING', 'ACTION_BASED'}\n" ] } ], "source": [ "print(problem.kind.features)" ] }, { "cell_type": "markdown", "metadata": { "id": "J3tblkI9yEnW" }, "source": [ "#### Beyond plan generation" ] }, { "cell_type": "markdown", "metadata": { "id": "xbY7bAPByL35" }, "source": [ "`OneshotPlanner` is not the only operation mode we can invoke from the unified_planning, it is just one way to interact with a planning engine. Another useful functionality is `PlanValidation` that checks if a plan is valid for a problem." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "id": "p5s7ZwhzyPKG" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[96m *** Credits ***\n", "\u001b[0m\u001b[96m * In operation mode `PlanValidator` at line 2 of `/tmp/ipykernel_154533/2705118315.py`, \u001b[0m\u001b[96myou are using the following planning engine:\n", "\u001b[0m\u001b[96m * Engine name: Tamer\n", " * Developers: FBK Tamer Development Team\n", "\u001b[0m\u001b[96m * Description: \u001b[0m\u001b[96mTamer offers the capability to generate a plan for classical, numerical and temporal problems.\n", " * For those kind of problems tamer also offers the possibility of validating a submitted plan.\u001b[0m\u001b[96m\n", "\u001b[0m\u001b[96m\n", "\u001b[0mThe plan is valid\n" ] } ], "source": [ "plan = result.plan\n", "with PlanValidator(problem_kind=problem.kind, plan_kind=plan.kind) as validator:\n", " if validator.validate(problem, plan):\n", " print('The plan is valid')\n", " else:\n", " print('The plan is invalid')" ] }, { "cell_type": "markdown", "metadata": { "id": "FtY51vyASTcp" }, "source": [ "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.\n", "\n", "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)." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "id": "2mTQ3DlrSoRk" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[96m *** Credits ***\n", "\u001b[0m\u001b[96m * In operation mode `Compiler` at line 1 of `/tmp/ipykernel_154533/1981797297.py`, \u001b[0m\u001b[96myou are using the following planning engine:\n", "\u001b[0m\u001b[96m * Engine name: pyperplan\n", " * Developers: Albert-Ludwigs-Universität Freiburg (Yusra Alkhazraji, Matthias Frorath, Markus Grützner, Malte Helmert, Thomas Liebetraut, Robert Mattmüller, Manuela Ortlieb, Jendrik Seipp, Tobias Springenberg, Philip Stahl, Jan Wülfing)\n", "\u001b[0m\u001b[96m * Description: \u001b[0m\u001b[96mPyperplan is a lightweight STRIPS planner written in Python.\u001b[0m\u001b[96m\n", "\u001b[0m\u001b[96m\n", "\u001b[0mproblem name = robot\n", "\n", "types = [Location]\n", "\n", "fluents = [\n", " bool robot_at[l=Location]\n", " bool connected[l_from=Location, l_to=Location]\n", "]\n", "\n", "actions = [\n", " action move_l1_l2 {\n", " preconditions = [\n", " robot_at(l1)\n", " ]\n", " effects = [\n", " robot_at(l2) := true\n", " robot_at(l1) := false\n", " ]\n", " }\n", " action move_l0_l1 {\n", " preconditions = [\n", " robot_at(l0)\n", " ]\n", " effects = [\n", " robot_at(l1) := true\n", " robot_at(l0) := false\n", " ]\n", " }\n", " action move_l8_l9 {\n", " preconditions = [\n", " robot_at(l8)\n", " ]\n", " effects = [\n", " robot_at(l9) := true\n", " robot_at(l8) := false\n", " ]\n", " }\n", " action move_l7_l8 {\n", " preconditions = [\n", " robot_at(l7)\n", " ]\n", " effects = [\n", " robot_at(l8) := true\n", " robot_at(l7) := false\n", " ]\n", " }\n", " action move_l6_l7 {\n", " preconditions = [\n", " robot_at(l6)\n", " ]\n", " effects = [\n", " robot_at(l7) := true\n", " robot_at(l6) := false\n", " ]\n", " }\n", " action move_l3_l4 {\n", " preconditions = [\n", " robot_at(l3)\n", " ]\n", " effects = [\n", " robot_at(l4) := true\n", " robot_at(l3) := false\n", " ]\n", " }\n", " action move_l4_l5 {\n", " preconditions = [\n", " robot_at(l4)\n", " ]\n", " effects = [\n", " robot_at(l5) := true\n", " robot_at(l4) := false\n", " ]\n", " }\n", " action move_l5_l6 {\n", " preconditions = [\n", " robot_at(l5)\n", " ]\n", " effects = [\n", " robot_at(l6) := true\n", " robot_at(l5) := false\n", " ]\n", " }\n", " action move_l2_l3 {\n", " preconditions = [\n", " robot_at(l2)\n", " ]\n", " effects = [\n", " robot_at(l3) := true\n", " robot_at(l2) := false\n", " ]\n", " }\n", "]\n", "\n", "objects = [\n", " Location: [l0, l1, l2, l3, l4, l5, l6, l7, l8, l9]\n", "]\n", "\n", "initial fluents default = [\n", " bool robot_at[l=Location] := false\n", " bool connected[l_from=Location, l_to=Location] := false\n", "]\n", "\n", "initial values = [\n", " robot_at(l0) := true\n", "]\n", "\n", "goals = [\n", " robot_at(l9)\n", "]\n", "\n", "\n", "\u001b[96m *** Credits ***\n", "\u001b[0m\u001b[96m * In operation mode `OneshotPlanner` at line 7 of `/tmp/ipykernel_154533/1981797297.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[0mGround plan: SequentialPlan:\n", " move_l0_l1\n", " move_l1_l2\n", " move_l2_l3\n", " move_l3_l4\n", " move_l4_l5\n", " move_l5_l6\n", " move_l6_l7\n", " move_l7_l8\n", " move_l8_l9\n", "Lifted plan: SequentialPlan:\n", " move(l0, l1)\n", " move(l1, l2)\n", " move(l2, l3)\n", " move(l3, l4)\n", " move(l4, l5)\n", " move(l5, l6)\n", " move(l6, l7)\n", " move(l7, l8)\n", " move(l8, l9)\n", "\u001b[96m *** Credits ***\n", "\u001b[0m\u001b[96m * In operation mode `PlanValidator` at line 14 of `/tmp/ipykernel_154533/1981797297.py`, \u001b[0m\u001b[96myou are using the following planning engine:\n", "\u001b[0m\u001b[96m * Engine name: Tamer\n", " * Developers: FBK Tamer Development Team\n", "\u001b[0m\u001b[96m * Description: \u001b[0m\u001b[96mTamer offers the capability to generate a plan for classical, numerical and temporal problems.\n", " * For those kind of problems tamer also offers the possibility of validating a submitted plan.\u001b[0m\u001b[96m\n", "\u001b[0m\u001b[96m\n", "\u001b[0m" ] } ], "source": [ "with Compiler(problem_kind=problem.kind, compilation_kind=CompilationKind.GROUNDING) as grounder:\n", " grounding_result = grounder.compile(problem, CompilationKind.GROUNDING)\n", " ground_problem = grounding_result.problem\n", " print(ground_problem)\n", "\n", " # The grounding_result can be used to \"lift\" a ground plan back to the level of the original problem\n", " with OneshotPlanner(problem_kind=ground_problem.kind) as planner:\n", " ground_plan = planner.solve(ground_problem).plan\n", " print('Ground plan: %s' % ground_plan)\n", " # Replace the action instances of the grounded plan with their correspoding lifted version\n", " lifted_plan = ground_plan.replace_action_instances(grounding_result.map_back_action_instance)\n", " print('Lifted plan: %s' % lifted_plan)\n", " # Test the problem and plan validity\n", " with PlanValidator(problem_kind=problem.kind, plan_kind=ground_plan.kind) as validator:\n", " ground_validation = validator.validate(ground_problem, ground_plan)\n", " lift_validation = validator.validate(problem, lifted_plan)\n", " Valid = up.engines.ValidationResultStatus.VALID\n", " assert ground_validation.status == Valid\n", " assert lift_validation.status == Valid" ] }, { "cell_type": "markdown", "metadata": { "id": "bbVeET7FyVB3" }, "source": [ "#### Parallel planning" ] }, { "cell_type": "markdown", "metadata": { "id": "16WuqVp3yX9j" }, "source": [ "We can invoke different instances of a planner in parallel or different planners and return the first plan that is generated effortlessly." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "id": "aeUm0TPZya7e" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[96m *** Credits ***\n", "\u001b[0m\u001b[96m * In operation mode `OneshotPlanner` at line 1 of `/tmp/ipykernel_154533/3703753692.py`, \u001b[0m\u001b[96myou are using a parallel planning engine with the following components:\n", "\u001b[0m\u001b[96m * Engine name: Tamer\n", " * Developers: FBK Tamer Development Team\n", "\u001b[0m\u001b[96m * Description: \u001b[0m\u001b[96mTamer offers the capability to generate a plan for classical, numerical and temporal problems.\n", " * For those kind of problems tamer also offers the possibility of validating a submitted plan.\u001b[0m\u001b[96m\n", "\u001b[0m\u001b[96m * Engine name: Tamer\n", " * Developers: FBK Tamer Development Team\n", "\u001b[0m\u001b[96m * Description: \u001b[0m\u001b[96mTamer offers the capability to generate a plan for classical, numerical and temporal problems.\n", " * For those kind of problems tamer also offers the possibility of validating a submitted plan.\u001b[0m\u001b[96m\n", "\u001b[0m\u001b[96m * Engine name: pyperplan\n", " * Developers: Albert-Ludwigs-Universität Freiburg (Yusra Alkhazraji, Matthias Frorath, Markus Grützner, Malte Helmert, Thomas Liebetraut, Robert Mattmüller, Manuela Ortlieb, Jendrik Seipp, Tobias Springenberg, Philip Stahl, Jan Wülfing)\n", "\u001b[0m\u001b[96m * Description: \u001b[0m\u001b[96mPyperplan is a lightweight STRIPS planner written in Python.\u001b[0m\u001b[96m\n", "\u001b[0m\u001b[96m\n", "\u001b[0mParallel returned: SequentialPlan:\n", " move(l0, l1)\n", " move(l1, l2)\n", " move(l2, l3)\n", " move(l3, l4)\n", " move(l4, l5)\n", " move(l5, l6)\n", " move(l6, l7)\n", " move(l7, l8)\n", " move(l8, l9)\n" ] } ], "source": [ "with OneshotPlanner(names=['tamer', 'tamer', 'pyperplan'],\n", " params=[{'heuristic': 'hadd'}, {'heuristic': 'hmax'}, {}]) as planner:\n", " plan = planner.solve(problem).plan\n", " print(\"%s returned: %s\" % (planner.name, plan))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": false }, "outputs": [], "source": [ "from unified_planning.plot import plot_sequential_plan" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ignore the code below, it's used to make this notebook also runnable in the Countinuous Intergation." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Redefine the plot package methods imported above to print the plot to a temp file\n", "# if the exception \"could not locate runnable browser\" is raised. This usually happens\n", "# in the Continuous Integration.\n", "\n", "from inspect import getmembers, isfunction\n", "from unified_planning import plot\n", "from functools import partial\n", "import os, uuid, tempfile as tf\n", "\n", "# Define the function that will be executed instead\n", "def _function(original_function, *args, **kwargs):\n", " try:\n", " original_function(*args, **kwargs)\n", " except Exception as e:\n", " if \"could not locate runnable browser\" in str(e):\n", " original_function(*args, **kwargs,\n", " filename=f\"{os.path.join(tf.gettempdir(), str(uuid.uuid1()))}.png\"\n", " )\n", " else:\n", " raise e\n", "\n", "# Iterate over all the functions of the plot package\n", "for function_name, function in getmembers(plot, isfunction):\n", " # Override the original function with the new one\n", " globals()[function_name] = partial(_function, function)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if plan is not None:\n", " plot_sequential_plan(plan, figsize=(16, 4), node_size=4000, font_size=10)" ] } ], "metadata": { "celltoolbar": "Tags", "colab": { "collapsed_sections": [], "name": "UP Basics", "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" }, "vscode": { "interpreter": { "hash": "fcfc934ecfdac8ddac62d6a80ba8d82faf47dc8d54fd6a313f0c016b85ebec0e" } } }, "nbformat": 4, "nbformat_minor": 1 }