{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "vXUqFpLObzhb" }, "source": [ "## Numeric Planning\n", "\n", "This python notebook shows how to use the unified planning library to model a simple numeric planning problem.\n", "\n", "[![Open In GitHub](https://img.shields.io/badge/see-Github-579aca?logo=github)](https:///github.com/aiplan4eu/unified-planning/blob/master/docs/notebooks/01-numeric-planning.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-numeric-planning.ipynb)\n" ] }, { "cell_type": "markdown", "metadata": { "id": "t8dCcpf7mivV" }, "source": [ "### Setup\n", "\n", "We install java (used for the numeric planner ENHSP) and the unified planning library." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "BoqALxJWdfl8", "outputId": "299282e7-07f5-46df-e607-47da781dca50", "scrolled": true, "tags": [ "remove_from_CI" ] }, "outputs": [], "source": [ "!apt-get install openjdk-17-jdk\n", "%pip install unified-planning[enhsp]" ] }, { "cell_type": "markdown", "metadata": { "id": "iNHFHxQKnKIp" }, "source": [ "We are now ready to use the Unified-Planning library!" ] }, { "cell_type": "markdown", "metadata": { "id": "xI2BGgmvdsek" }, "source": [ "### Demo\n" ] }, { "cell_type": "markdown", "metadata": { "id": "xn5l-SVxufFA" }, "source": [ "We start importing the shortcuts." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "otZVSku3idJC" }, "outputs": [], "source": [ "from unified_planning.shortcuts import *" ] }, { "cell_type": "markdown", "metadata": { "id": "M9DCTuoSu2vh" }, "source": [ "Now we start to model a problem involving three numeric variables $c_0$, $c_1$ and $c_2$ that can be increased and decreased. The goal of this problem is to change the variables values such that $c_0 < c_1 < c_2$. We name with value the lifted fluent that lets us access to the value of a given counter $c$.\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "id": "nBaUofy3Ko7V" }, "source": [ "\n", "#### Creating the fluent\n", "\n", "First, we define the `UserTypes` and the `Fluents`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "e5BWi6wyuqyB" }, "outputs": [], "source": [ "Counter = UserType('Counter')\n", "\n", "value = Fluent('value', IntType(), m=Counter)" ] }, { "cell_type": "markdown", "metadata": { "id": "5sJwsWGy3-K5" }, "source": [ "#### Creating the actions\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "J-cU6CvL0-Pv" }, "outputs": [], "source": [ "inc = InstantaneousAction('increment',c=Counter)\n", "c = inc.parameter('c')\n", "inc.add_precondition(LE(value(c), 10))\n", "inc.add_increase_effect(value(c), 1)\n", "\n", "dec = InstantaneousAction('decrement',c=Counter)\n", "c = dec.parameter('c')\n", "dec.add_precondition(GT(value(c), 0))\n", "dec.add_decrease_effect(value(c),1)\n" ] }, { "cell_type": "markdown", "metadata": { "id": "aMMtRDVovvuM" }, "source": [ "Finally, we can create a `Problem` that encompasses the fluents and the actions, and puts them together with concrete objects, an initial state and a goal. Note here that we do not need to specify all values for each object. These are set to 0 using the default intial value parameter.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "vEDxcqkLvm9d", "outputId": "aae981f7-dac8-4a2e-e02a-0619edefd271" }, "outputs": [ { "data": { "text/plain": [ "problem name = problem\n", "\n", "types = [Counter]\n", "\n", "fluents = [\n", " integer value[m=Counter]\n", "]\n", "\n", "actions = [\n", " action increment(Counter c) {\n", " preconditions = [\n", " (value(c) <= 10)\n", " ]\n", " effects = [\n", " value(c) += 1\n", " ]\n", " }\n", " action decrement(Counter c) {\n", " preconditions = [\n", " (0 < value(c))\n", " ]\n", " effects = [\n", " value(c) -= 1\n", " ]\n", " }\n", "]\n", "\n", "objects = [\n", " Counter: [c0, c1, c2]\n", "]\n", "\n", "initial fluents default = [\n", " integer value[m=Counter] := 0\n", "]\n", "\n", "initial values = [\n", "]\n", "\n", "goals = [\n", " (((value(c1) + 1) <= value(c2)) and ((value(c0) + 1) <= value(c1)))\n", "]\n" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "problem = Problem('problem')\n", "\n", "problem.add_fluent(value, default_initial_value=0)\n", "C0 = Object('c0', Counter)\n", "C1 = Object('c1', Counter)\n", "C2 = Object('c2', Counter)\n", "problem.add_object(C0)\n", "problem.add_object(C1)\n", "problem.add_object(C2)\n", "problem.add_action(inc)\n", "problem.add_action(dec)\n", "problem.add_goal(And( GE(value(C2),Plus(value(C1),1)), GE(value(C1),Plus(value(C0),1))))\n", "problem\n" ] }, { "cell_type": "markdown", "metadata": { "id": "Pfo5MK-4FIs4" }, "source": [ "\n", "Now we see how we can generate another, larger problem, much more compactly using a more programmatic definition\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "v62jmabZCyZr", "outputId": "e44cd067-6ceb-4db1-b939-34c79248f64c" }, "outputs": [ { "data": { "text/plain": [ "problem name = Large_problems\n", "\n", "types = [Counter]\n", "\n", "fluents = [\n", " integer value[m=Counter]\n", "]\n", "\n", "actions = [\n", " action increment(Counter c) {\n", " preconditions = [\n", " (value(c) <= 10)\n", " ]\n", " effects = [\n", " value(c) += 1\n", " ]\n", " }\n", " action decrement(Counter c) {\n", " preconditions = [\n", " (0 < value(c))\n", " ]\n", " effects = [\n", " value(c) -= 1\n", " ]\n", " }\n", "]\n", "\n", "objects = [\n", " Counter: [c0, c1, c2, c3, c4, c5, c6, c7, c8]\n", "]\n", "\n", "initial fluents default = [\n", " integer value[m=Counter] := 0\n", "]\n", "\n", "initial values = [\n", "]\n", "\n", "goals = [\n", " ((value(c0) + 1) <= value(c1))\n", " ((value(c1) + 1) <= value(c2))\n", " ((value(c2) + 1) <= value(c3))\n", " ((value(c3) + 1) <= value(c4))\n", " ((value(c4) + 1) <= value(c5))\n", " ((value(c5) + 1) <= value(c6))\n", " ((value(c6) + 1) <= value(c7))\n", " ((value(c7) + 1) <= value(c8))\n", "]\n" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "N = 9 # This is the number of counters\n", "\n", "p2 = Problem('Large_problems')\n", "\n", "p2.add_fluent(value, default_initial_value=0)\n", "p2.add_objects([Object(f'c{i}',Counter) for i in range(N)])\n", "p2.add_action(inc)\n", "p2.add_action(dec)\n", "\n", "for i in range(N-1):\n", " p2.add_goal(GE(value(p2.object(f'c{i+1}')),Plus(value(p2.object(f'c{i}')),1)))\n", "\n", "p2" ] }, { "cell_type": "markdown", "metadata": { "id": "z3l0y9kaKMII" }, "source": [ "#### Solving the small and the parametric problem\n", "\n", "The unified_planning can either select among the available planners one which is suited for the task at hand (looking at the problem kind), or use the user defined planning. In what follows we first attempt to solve the small problem with three counters and ask the UP to use a specific planning system (ENHSP), and then one with N=9 counters (problem p2) asking the UP to automatically select the engine\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "frcEeD-oKO2m", "outputId": "1ea9a630-db3f-47d8-b011-85e97be31203" }, "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 ``, \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[0mSAT-enhsp returned:\n", "SequentialPlan:\n", " increment(c2)\n", " increment(c1)\n", " increment(c2)\n" ] } ], "source": [ "with OneshotPlanner(name='enhsp') as planner:\n", " result = planner.solve(problem)\n", " plan = result.plan\n", " if plan is not None:\n", " print(\"%s returned:\" % planner.name)\n", " print(plan)\n", " else:\n", " print(\"No plan found.\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "1Xz-IqYoE56M", "outputId": "8a4b48ec-06e7-41f4-837d-c4c2c0564d14" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[96m *** Credits ***\n", "\u001b[0m\u001b[96m * In operation mode `OneshotPlanner` at line 1 of ``, \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[0mSAT-enhsp returned:\n", "SequentialPlan:\n", " increment(c8)\n", " increment(c7)\n", " increment(c8)\n", " increment(c8)\n", " increment(c7)\n", " increment(c6)\n", " increment(c8)\n", " increment(c5)\n", " increment(c6)\n", " increment(c7)\n", " increment(c8)\n", " increment(c4)\n", " increment(c5)\n", " increment(c6)\n", " increment(c7)\n", " increment(c8)\n", " increment(c8)\n", " increment(c3)\n", " increment(c4)\n", " increment(c5)\n", " increment(c7)\n", " increment(c6)\n", " increment(c7)\n", " increment(c8)\n", " increment(c6)\n", " increment(c5)\n", " increment(c4)\n", " increment(c2)\n", " increment(c3)\n", " increment(c7)\n", " increment(c6)\n", " increment(c5)\n", " increment(c4)\n", " increment(c3)\n", " increment(c1)\n", " increment(c2)\n" ] } ], "source": [ "with OneshotPlanner(problem_kind=problem.kind) as planner:\n", " result = planner.solve(p2)\n", " plan = result.plan\n", " if plan is not None:\n", " print(\"%s returned:\" % planner.name)\n", " print(plan)\n", " else:\n", " print(\"No plan found.\")" ] }, { "cell_type": "markdown", "metadata": { "id": "YGfAogQpHZY-" }, "source": [ "Now let us create a problem medium-sized problem, set up a minimisation function as minimize the number of actions, and see how this can be solved optimally." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "v41WrIcmH1eJ", "outputId": "e80e8b7b-dfb1-4d88-84a2-856c0ba9a50f" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[96m *** Credits ***\n", "\u001b[0m\u001b[96m * In operation mode `OneshotPlanner` at line 17 of ``, \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[0mOPT-enhsp returned:\n", "SequentialPlan:\n", " increment(c6)\n", " increment(c5)\n", " increment(c4)\n", " increment(c3)\n", " increment(c6)\n", " increment(c5)\n", " increment(c4)\n", " increment(c3)\n", " increment(c6)\n", " increment(c5)\n", " increment(c4)\n", " increment(c6)\n", " increment(c5)\n", " increment(c2)\n", " increment(c6)\n", " increment(c6)\n", " increment(c5)\n", " increment(c4)\n", " increment(c3)\n", " increment(c2)\n", " increment(c1)\n" ] } ], "source": [ "from unified_planning.model.metrics import MinimizeSequentialPlanLength\n", "\n", "N = 7 #This is the number of counters\n", "\n", "mediumSizeProblem = Problem('Medium_sized_problem')\n", "\n", "mediumSizeProblem.add_fluent(value, default_initial_value=0)\n", "mediumSizeProblem.add_objects([Object(f'c{i}',Counter) for i in range(N)])\n", "mediumSizeProblem.add_action(inc)\n", "mediumSizeProblem.add_action(dec)\n", "metric = MinimizeSequentialPlanLength()\n", "mediumSizeProblem.add_quality_metric(metric)\n", "\n", "for i in range(N-1):\n", " mediumSizeProblem.add_goal(GE(value(p2.object(f'c{i+1}')),Plus(value(p2.object(f'c{i}')),1)))\n", "\n", "with OneshotPlanner(problem_kind=problem.kind,optimality_guarantee=True) as planner:\n", " result = planner.solve(mediumSizeProblem)\n", " plan = result.plan\n", " if plan is not None:\n", " print(\"%s returned:\" % planner.name)\n", " print(plan)\n", " else:\n", " print(\"No plan found.\")\n" ] } ], "metadata": { "celltoolbar": "Tags", "colab": { "name": "Numeric Planning", "provenance": [] }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.6" } }, "nbformat": 4, "nbformat_minor": 1 }