10. Multi-Agent Plan Simple Example

This python notebook shows how to use the unified planning library to model multi-agent problems.

Open In GitHub Open In Colab

10.1. Setup

We start by installing the unified planning library and a multi-agent planner (FMAP).

[1]:
!pip install unified-planning[fmap]

We are now ready to use the Unified-Planning library!

10.2. Demo

In this demo we show how to model a multi-agent planning problem using the Unified Planning library.

10.2.1. Basic imports

[2]:
from unified_planning.shortcuts import *
from unified_planning.model.multi_agent import *
from collections import namedtuple
from unified_planning.io.ma_pddl_writer import MAPDDLWriter

10.2.2. Creating the simple-MA problem

The class that represents a multi-agent planning problem is unified_planning.MultiAgentProblem, it contains the set of agents, the objects, an intial value for all the fluents and a goal to be reached by the planner. We create a MultiAgentProblem and two Agents. An Agent is an individual entity with specific fluents and specific actions.

[3]:
problem = MultiAgentProblem("simple_MA")

#AGENTs
robot_a = Agent("robot_a", problem)
scale_a = Agent("scale_a", problem)

We define the UserTypes, the Objects, the Fluents and the Actions

[4]:
#USERTYPEs
Location = UserType("Location")
door = UserType("door")

#FLUENTs
open = Fluent("open", door=door)
pos = Fluent("pos", loc=Location)

#OBJECTs
home = Object("home", Location)
office = Object("office", Location)
open20 = Object("open20", door)
close20 = Object("close20", door)

#ACTIONs
movegripper = InstantaneousAction("movegripper")
movegripper.add_precondition(pos(office))
movegripper.add_effect(pos(home), True)

open_door = InstantaneousAction("open_door")
open_door.add_precondition(open(close20))
open_door.add_effect(open(open20), True)

Let’s add the Fluents to the Agents. Note: An agent’s fluent can be of two types, public or private. Public Fluents are visible to other agents. In contrast, the Private Fluents are not visible from the other agents. Private fluents are added to the agent via the add_fluent or add_private_fluent methods and public fluents via the add_public_fluent method.

[5]:
robot_a.add_fluent(pos, default_initial_value=False)
scale_a.add_fluent(open, default_initial_value=False)
[5]:
bool open[door=door]

We add to the agents the actions they can perform.

[6]:
robot_a.add_action(movegripper)
scale_a.add_action(open_door)

Let’s add the agents to the MultiAgentProblem.

[7]:
problem.add_agent(robot_a)
problem.add_agent(scale_a)

We add the objects, the initial values and the goals. Note: Dot operator is used to denote agent-specific Fluents.

[8]:
#OBJECTs
problem.add_object(home)
problem.add_object(office)
problem.add_object(open20)
problem.add_object(close20)

#INITIAL VALUEs
problem.set_initial_value(Dot(robot_a, pos(office)), True)
problem.set_initial_value(Dot(scale_a, open(close20)), True)

#GOALs
problem.add_goal(Dot(robot_a, pos(home)))
problem.add_goal(Dot(scale_a, open(open20)))

10.2.3. MA-PDDL Writer

To write the ma-pddl equivalent of a unified_planning MultiAgentProblem to a file we use the MAPDDLWriter.write_ma_domain and MAPDDLWriter.write_ma_problem methods.

[9]:
w = MAPDDLWriter(problem)
w.write_ma_domain("simple_ma")
w.write_ma_problem("simple_ma")

10.3. Solving Multi-Agent Planning Problems

We can use fmap to solve a Multi-Agent problem. It allows the specification of a specific heuristic function used to evaluate the quality of the plans. The name of the custom parameter is heuristic and the following values are supported:

  • 0 - FF heuristic: guides the search through the well-known h_FF heuristic function. This option is available for single-agent planning tasks only.

  • 1 - DTG heuristic: evaluates plans via the heuristic h_DTG.

  • 2 - default option - DTG + Landmarks: this option applies the multi-heuristic search scheme of the MH-FMAP solver by combining the h_DTG and h_Land heuristics to guide the search.

  • 3 - Inc. DTG + Landmarks: incremental multi-heuristic mode that makes use of h_DTG and h_Land.

[10]:
with OneshotPlanner(name='fmap', params={'heuristic': '1'}) as planner:
    result = planner.solve(problem)
    if result.status == up.engines.PlanGenerationResultStatus.SOLVED_SATISFICING:
        print("%s Returned Sequential Plans object: %s" % (planner.name, result.plan.all_sequential_plans()))
        [print(f"{idx} Sequential Plans: {seq_plan}") for idx, seq_plan in enumerate(result.plan.all_sequential_plans())]
        print("Adjacency list:", result.plan.get_adjacency_list)
        print("result:", result)
    else:
        print("Log Error:", result)
NOTE: To disable printing of planning engine credits, add this line to your code: `up.shortcuts.get_environment().credits_stream = None`
  *** Credits ***
  * In operation mode `OneshotPlanner` at line 1 of `/tmp/ipykernel_157727/678844819.py`, you are using the following planning engine:
  * Engine name: FMAP
  * Developers:  Alejandro Torreño, Oscar Sapena and Eva Onaindia
  * Description: FMAP: A Platform for the Development of Distributed Multi-Agent Planning Systems.

FMAP Returned Sequential Plans object: <generator object PartialOrderPlan.all_sequential_plans at 0x7fec001b1d90>
0 Sequential Plans: SequentialPlan:
    scale_a.open_door
    robot_a.movegripper
1 Sequential Plans: SequentialPlan:
    robot_a.movegripper
    scale_a.open_door
Adjacency list: {robot_a.movegripper: [], scale_a.open_door: []}
result: status: SOLVED_SATISFICING
engine: FMAP
plan: PartialOrderPlan:
  actions:
    0) robot_a.movegripper
    1) scale_a.open_door
  constraints: