10. Multi-Agent Plan Simple Example
This python notebook shows how to use the unified planning library to model multi-agent problems.
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: