Skip to content

Commit

Permalink
Merge pull request #473 from aiplan4eu/planner-integration-notebook
Browse files Browse the repository at this point in the history
update planner integration notebook
  • Loading branch information
alvalentini committed Aug 10, 2023
2 parents 6b51ec1 + 513da68 commit 02f25aa
Showing 1 changed file with 51 additions and 52 deletions.
103 changes: 51 additions & 52 deletions docs/notebooks/planner-integration.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@
"[![Open In GitHub](https://img.shields.io/badge/see-Github-579aca?logo=github)](https://github.com/aiplan4eu/unified-planning/blob/master/docs/notebooks/planner-integration.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/planner-integration.ipynb)\n",
"\n",
"**_NOTE:_** The unified planning library has auxiliary base classes for **integrating stand-alone PDDL planners** (not described in this notebook). They take care of writing the PDDL files, calling your planner and collecting the plan from disk on various operating systems.\n",
"For oneshot planning, use [`engines.PDDLPlanner`](https://github.com/aiplan4eu/unified-planning/blob/master/unified_planning/engines/pddl_planner.py). For anytime planning there is [`engines.PDDLAnytimePlanner`](https://github.com/aiplan4eu/unified-planning/blob/master/unified_planning/engines/pddl_anytime_planner.py).\n",
"\n",
"## Setup\n",
"We start by installing the library with PIP"
]
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"metadata": {
"id": "BoqALxJWdfl8",
"tags": [
Expand All @@ -28,7 +31,7 @@
},
"outputs": [],
"source": [
"!pip install --pre unified-planning"
"!pip install unified-planning"
]
},
{
Expand All @@ -49,25 +52,23 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 2,
"metadata": {
"id": "01nDJbkoVZU1"
},
"outputs": [],
"source": [
"import random\n",
"from typing import Optional, Callable, IO\n",
"from unified_planning.engines.results import PlanGenerationResultStatus\n",
"from typing import Callable, IO, Optional\n",
"import unified_planning as up\n",
"import unified_planning.engines as engines\n",
"from unified_planning.model import ProblemKind\n",
"from unified_planning import engines\n",
"\n",
"class MySolverImpl(engines.Engine,\n",
" engines.mixins.OneshotPlannerMixin):\n",
"class MySolverImpl(up.engines.Engine,\n",
" up.engines.mixins.OneshotPlannerMixin):\n",
" def __init__(self, **options):\n",
" # Read known user-options and store them for using in the `solve` method\n",
" engines.Engine.__init__(self)\n",
" engines.mixins.OneshotPlannerMixin.__init__(self)\n",
" up.engines.Engine.__init__(self)\n",
" up.engines.mixins.OneshotPlannerMixin.__init__(self)\n",
" self.max_tries = options.get('max_tries', None)\n",
" self.restart_probability = options.get('restart_probability', 0.00001)\n",
"\n",
Expand All @@ -80,12 +81,15 @@
" # For this demo we limit ourselves to numeric planning.\n",
" # Other kinds of problems can be modeled in the UP library,\n",
" # see unified_planning.model.problem_kind.\n",
" supported_kind = ProblemKind()\n",
" supported_kind = up.model.ProblemKind()\n",
" supported_kind.set_problem_class(\"ACTION_BASED\")\n",
" supported_kind.set_problem_type(\"GENERAL_NUMERIC_PLANNING\")\n",
" supported_kind.set_typing('FLAT_TYPING')\n",
" supported_kind.set_typing('HIERARCHICAL_TYPING')\n",
" supported_kind.set_numbers('CONTINUOUS_NUMBERS')\n",
" supported_kind.set_numbers('DISCRETE_NUMBERS')\n",
" supported_kind.set_fluents_type('NUMERIC_FLUENTS')\n",
" supported_kind.set_numbers('BOUNDED_TYPES')\n",
" supported_kind.set_fluents_type('OBJECT_FLUENTS')\n",
" supported_kind.set_conditions_kind('NEGATIVE_CONDITIONS')\n",
" supported_kind.set_conditions_kind('DISJUNCTIVE_CONDITIONS')\n",
Expand All @@ -95,6 +99,8 @@
" supported_kind.set_effects_kind('CONDITIONAL_EFFECTS')\n",
" supported_kind.set_effects_kind('INCREASE_EFFECTS')\n",
" supported_kind.set_effects_kind('DECREASE_EFFECTS')\n",
" supported_kind.set_effects_kind('FLUENTS_IN_NUMERIC_ASSIGNMENTS')\n",
"\n",
" return supported_kind\n",
"\n",
" @staticmethod\n",
Expand All @@ -104,12 +110,12 @@
" def _solve(self, problem: 'up.model.Problem',\n",
" callback: Optional[Callable[['up.engines.PlanGenerationResult'], None]] = None,\n",
" timeout: Optional[float] = None,\n",
" output_stream: Optional[IO[str]] = None) -> 'up.engines.results.PlanGenerationResult':\n",
" output_stream: Optional[IO[str]] = None) -> 'up.engines.PlanGenerationResult':\n",
" env = problem.environment\n",
"\n",
" # First we ground the problem\n",
" with env.factory.Compiler(problem_kind=problem.kind, compilation_kind=engines.CompilationKind.GROUNDING) as grounder:\n",
" grounding_result = grounder.compile(problem, engines.CompilationKind.GROUNDING)\n",
" with env.factory.Compiler(problem_kind=problem.kind, compilation_kind=up.engines.CompilationKind.GROUNDING) as grounder:\n",
" grounding_result = grounder.compile(problem, up.engines.CompilationKind.GROUNDING)\n",
" grounded_problem = grounding_result.problem\n",
" \n",
" # We store the grounded actions in a list\n",
Expand Down Expand Up @@ -143,7 +149,8 @@
" resplan = plan.replace_action_instances(grounding_result.map_back_action_instance)\n",
" # Sanity check\n",
" assert pv.validate(problem, resplan)\n",
" return up.engines.PlanGenerationResult(PlanGenerationResultStatus.SOLVED_SATISFICING, resplan, self.name)\n",
" status = up.engines.PlanGenerationResultStatus.SOLVED_SATISFICING\n",
" return up.engines.PlanGenerationResult(status, resplan, self.name)\n",
" else:\n",
" # If the plan is invalid, check if the reason is action\n",
" # applicability (as opposed to goal satisfaction)\n",
Expand All @@ -154,7 +161,8 @@
" # Limit the number of tries, according to the user specification\n",
" counter += 1\n",
" if self.max_tries is not None and counter >= self.max_tries:\n",
" return up.engines.PlanGenerationResult(PlanGenerationResultStatus.TIMEOUT, None, self.name)\n",
" status = up.engines.PlanGenerationResultStatus.TIMEOUT\n",
" return up.engines.PlanGenerationResult(status, None, self.name)\n",
"\n",
" def destroy(self):\n",
" pass"
Expand Down Expand Up @@ -182,7 +190,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 3,
"metadata": {
"id": "nY1d3eK7amBP"
},
Expand Down Expand Up @@ -216,35 +224,30 @@
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"id": "lEI3x-eTaA4s"
},
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"emgr = env.expression_manager\n",
"from unified_planning.shortcuts import * \n",
"\n",
"Location = env.type_manager.UserType('Location')\n",
"robot_at = up.model.Fluent('robot_at', env.type_manager.BoolType(), loc=Location)\n",
"battery_charge = up.model.Fluent('battery_charge', env.type_manager.RealType(0, 100))\n",
"move = up.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(emgr.GE(battery_charge, 10))\n",
"move.add_precondition(emgr.Not(emgr.Equals(l_from, l_to)))\n",
"problem = Problem('robot')\n",
"Location = UserType('Location')\n",
"robot_at = problem.add_fluent('robot_at', BoolType(), loc=Location)\n",
"battery_charge = problem.add_fluent('battery_charge', RealType(0, 100))\n",
"\n",
"move = InstantaneousAction('move', l_from=Location, l_to=Location)\n",
"l_from = move.l_from\n",
"l_to = move.l_to\n",
"move.add_precondition(battery_charge >= 10)\n",
"move.add_precondition(Not(Equals(l_from, l_to)))\n",
"move.add_precondition(robot_at(l_from))\n",
"move.add_precondition(emgr.Not(robot_at(l_to)))\n",
"move.add_effect(robot_at(l_from), False)\n",
"move.add_effect(robot_at(l_to), True)\n",
"move.add_effect(battery_charge, emgr.Minus(battery_charge, 10))\n",
"l1 = up.model.Object('l1', Location)\n",
"l2 = up.model.Object('l2', Location)\n",
"problem = up.model.Problem('robot')\n",
"problem.add_fluent(robot_at)\n",
"problem.add_fluent(battery_charge)\n",
"move.add_effect(battery_charge, battery_charge - 10)\n",
"problem.add_action(move)\n",
"problem.add_object(l1)\n",
"problem.add_object(l2)\n",
"\n",
"l1 = problem.add_object('l1', Location)\n",
"l2 = problem.add_object('l2', Location)\n",
"problem.set_initial_value(robot_at(l1), True)\n",
"problem.set_initial_value(robot_at(l2), False)\n",
"problem.set_initial_value(battery_charge, 100)\n",
Expand All @@ -262,35 +265,31 @@
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"id": "UVWTD-akbQ-P"
},
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"YOLOPlanner found a valid plan!\n",
"The plan is: [move(l2, l1)]\n",
"move(l2, l1)\n"
"SequentialPlan:\n",
" move(l1, l2)\n"
]
}
],
"source": [
"with env.factory.OneshotPlanner(name='yoloplanner') as p:\n",
"with OneshotPlanner(name='yoloplanner', params = {'max_tries' : 5}) as p:\n",
" result = p.solve(problem)\n",
" if result.status == PlanGenerationResultStatus.SOLVED_SATISFICING:\n",
" if result.status == up.engines.PlanGenerationResultStatus.SOLVED_SATISFICING:\n",
" print(f'{p.name} found a valid plan!')\n",
" print(f'The plan is: {result.plan}')\n",
" print('\\n'.join(str(x) for x in result.plan.actions))\n",
" print(result.plan)\n",
" else:\n",
" print('No plan found!')"
" print('No plan found!')\n"
]
}
],
"metadata": {
"celltoolbar": "Tags",
"colab": {
"collapsed_sections": [],
"name": "Planner Integration",
Expand Down

0 comments on commit 02f25aa

Please sign in to comment.