{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "ZHx6ZJ5OHimO", "slideshow": { "slide_type": "slide" } }, "source": [ "# 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." ] }, { "cell_type": "markdown", "metadata": { "id": "cRsOFdHXLozf", "slideshow": { "slide_type": "slide" } }, "source": [ "Let's first install the unified planning library." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "a7kQWSWSHgXl", "outputId": "62058f78-3dfc-4be5-ed71-ea97b9ffad27", "slideshow": { "slide_type": "fragment" }, "tags": [ "remove_from_CI" ] }, "outputs": [], "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": 1, "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": 2, "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": 3, "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": 4, "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": 5, "metadata": { "id": "yXOUGV0cqKUc", "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "dest_gabi = destination(gabi)\n", "arrived_gabi = arrived(gabi)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "print(arrived_gabi.__class__)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "bool arrived[person=Attendee - Person]" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "arrived_gabi.fluent()" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "(Gabi,)" ] }, "execution_count": 8, "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 the class [`Problem`](https://unified-planning.readthedocs.io/en/latest/api/model/Problem.html). " ] }, { "cell_type": "code", "execution_count": 9, "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": 10, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "fluent_at = problem.add_fluent(\"at\", location, p=person)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "Location at[p=Person]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# You can also get hold of a fluent from its name:\n", "problem.fluent(\"at\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Analogously for objects:" ] }, { "cell_type": "code", "execution_count": 12, "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": 13, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "[Gabi, Andrea, Arthur, Sebastian]" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(problem.objects(conference_attendee))" ] }, { "cell_type": "code", "execution_count": 14, "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": 15, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "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": [ "print(problem)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Let's set the (rest of the) initial state:" ] }, { "cell_type": "code", "execution_count": 16, "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": 17, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "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": [ "print(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": 18, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "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", "print(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": 19, "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", " ]\n", " effects = [\n", " at(p) := l_to\n", " ]\n", " }]\n" ] } ], "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", "print(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": 20, "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": 21, "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": 22, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "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": [ "print(problem.kind)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "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": 23, "metadata": { "slideshow": { "slide_type": "fragment" }, "tags": [ "raises-exception", "remove_from_CI" ] }, "outputs": [ { "ename": "UPNoSuitableEngineAvailableException", "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_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-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------" ] } ], "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": 24, "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": 25, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "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", "print(utr_problem)" ] }, { "cell_type": "code", "execution_count": 26, "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": 27, "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_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", "\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": 28, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PlanGenerationResultStatus.SOLVED_SATISFICING\n" ] } ], "source": [ "print(res.status)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "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", "print(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": [ { "name": "stdout", "output_type": "stream", "text": [ "PlanGenerationResultStatus.SOLVED_SATISFICING\n" ] } ], "source": [ "print(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_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", "\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": [ { "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": [ "print(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_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: enhsp\n", "plan: SequentialPlan:\n", " travel(Sebastian, Bremen, Prague)\n", " travel(Arthur, Toulouse, Prague)\n", " travel(Andrea, Trento, Prague)\n", " travel(Gabi, Basel, 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.12" } }, "nbformat": 4, "nbformat_minor": 1 }