{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "DALQzKA3Wh21" }, "source": [ "# Meta Engines\n", "\n", "In this notebook we define an oversubscription planning problem and we solve it using a `MetaEngine`.\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/06-oversubscription-with-metaengine.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/06-oversubscription-with-metaengine.ipynb)\n" ] }, { "cell_type": "markdown", "metadata": { "id": "JnUrQV84Pk4_" }, "source": [ "### Setup the library" ] }, { "cell_type": "markdown", "metadata": { "id": "WUlNZ8pVPk5A" }, "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": "fhMfqhBqPk5B", "tags": [ "remove_from_CI" ] }, "outputs": [], "source": [ "!apt install graphviz graphviz-dev\n", "%pip install unified-planning[tamer,plot]" ] }, { "cell_type": "markdown", "metadata": { "id": "p1p0OvjqW4g0" }, "source": [ "### Problem definition\n", "\n", "We model an oversubscription planning problem." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "id": "nc4DQP3kq-QP" }, "outputs": [], "source": [ "from unified_planning.shortcuts import *" ] }, { "cell_type": "markdown", "metadata": { "id": "JXDIDNwCm13b" }, "source": [ "We start the problem modeling defining the `UserType` and the `Fluent`." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "id": "nHXzkD2LmWkB" }, "outputs": [], "source": [ "Location = UserType('Location')\n", "Robot = UserType('Robot')\n", "\n", "at = Fluent('at', BoolType(), robot=Robot, location=Location)\n", "visited = Fluent('visited', BoolType(), robot=Robot, location=Location)\n", "connected = Fluent('connected', BoolType(), l_from=Location, l_to=Location)" ] }, { "cell_type": "markdown", "metadata": { "id": "OVUn5VPTmXKY" }, "source": [ "We define an action `move` that models the movement of a robot between two locations.\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "id": "cwXh99K2laqv" }, "outputs": [], "source": [ "move = InstantaneousAction('move', robot=Robot, l_from=Location, l_to=Location)\n", "robot = move.parameter('robot')\n", "l_from = move.parameter('l_from')\n", "l_to = move.parameter('l_to')\n", "move.add_precondition(at(robot, l_from))\n", "move.add_precondition(connected(l_from, l_to))\n", "move.add_effect(at(robot, l_from), False)\n", "move.add_effect(at(robot, l_to), True)\n", "move.add_effect(visited(robot, l_to), True)" ] }, { "cell_type": "markdown", "metadata": { "id": "C92YH1nrmRb7" }, "source": [ "We define the `Object` instances and, after creating the `Problem`, we set the initial values." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "id": "MczotRommR_k" }, "outputs": [], "source": [ "r1 = Object('r1', Robot)\n", "NLOC = 10\n", "locations = [Object('l%s' % i, Location) for i in range(NLOC)]\n", "\n", "problem = Problem('robot_with_simulated_effects')\n", "problem.add_fluent(at, default_initial_value=False)\n", "problem.add_fluent(visited, default_initial_value=False)\n", "problem.add_fluent(connected, default_initial_value=False)\n", "problem.add_action(move)\n", "\n", "problem.add_object(r1)\n", "problem.add_objects(locations)\n", "\n", "problem.set_initial_value(at(r1, locations[0]), True)\n", "problem.set_initial_value(visited(r1, locations[0]), True)\n", "for i in range(NLOC - 1):\n", " problem.set_initial_value(connected(locations[i], locations[i+1]), True)\n", "problem.set_initial_value(connected(locations[4], locations[8]), True)" ] }, { "cell_type": "markdown", "metadata": { "id": "57hCstd_ZzK5" }, "source": [ "Finally, we define the oversubscription goals." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "id": "IyVivfPuZzcd" }, "outputs": [], "source": [ "goals = {}\n", "goals[visited(r1, locations[5])] = -5\n", "goals[visited(r1, locations[7])] = 4\n", "goals[visited(r1, locations[9])] = 10\n", "\n", "problem.add_quality_metric(up.model.metrics.Oversubscription(goals))" ] }, { "cell_type": "markdown", "metadata": { "id": "pXvh83ljlabv" }, "source": [ "### Solving the problem" ] }, { "cell_type": "markdown", "metadata": { "id": "McV4znupqpkw" }, "source": [ "We solve the problem using the oversubscription `MetaEngine` with the tamer `Engine`." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "id": "bXHWJh2vl5RJ" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OversubscriptionPlanner[Tamer] returned: SequentialPlan:\n", " move(r1, l0, l1)\n", " move(r1, l1, l2)\n", " move(r1, l2, l3)\n", " move(r1, l3, l4)\n", " move(r1, l4, l8)\n", " move(r1, l8, l9)\n" ] } ], "source": [ "with OneshotPlanner(name='oversubscription[tamer]') as planner:\n", " result = planner.solve(problem)\n", " print(\"%s returned: %s\" % (planner.name, result.plan))" ] }, { "cell_type": "markdown", "metadata": { "id": "cUnXZCL4eNyj" }, "source": [ "To test the oversubscription `MetaEngine`, we update the oversubscription goals to see if it finds a different plan." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "id": "jJGRbo7weLUO" }, "outputs": [], "source": [ "problem.clear_quality_metrics()\n", "\n", "goals = {}\n", "goals[visited(r1, locations[5])] = -5\n", "goals[visited(r1, locations[7])] = 6\n", "goals[visited(r1, locations[9])] = 10\n", "\n", "problem.add_quality_metric(up.model.metrics.Oversubscription(goals))" ] }, { "cell_type": "markdown", "metadata": { "id": "jpKO5yUKdwlA" }, "source": [ "To solve the new problem, now we let the system choose the `Engine` to use." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "id": "HBqoZ3-ldyD1", "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OversubscriptionPlanner[Fast Downward] returned: SequentialPlan:\n", " move(r1, l0, l1)\n", " move(r1, l1, l2)\n", " move(r1, l2, l3)\n", " move(r1, l3, l4)\n", " move(r1, l4, l5)\n", " move(r1, l5, l6)\n", " move(r1, l6, l7)\n", " move(r1, l7, l8)\n", " move(r1, l8, l9)\n" ] } ], "source": [ "with OneshotPlanner(problem_kind=problem.kind) as planner:\n", " result = planner.solve(problem)\n", " plan = result.plan\n", " assert plan is not None\n", " print(\"%s returned: %s\" % (planner.name, result.plan))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "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": { "scrolled": true }, "outputs": [], "source": [ "plot_sequential_plan(plan, figsize=(18, 4), node_size=8000, font_size=11)" ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "Oversubscription using MetaEngines", "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 }