YODA - quasylab/sibilla GitHub Wiki
This page provides a companion for starting using YODA and its tools.
YODA (Yet anOther agent Description lAnguage) is a description language conceived to support specification and analysis of Multi-Agent Systems. The language permits describing both the behaviour of agents and the dynamics of the enclosing environment.
YODA has been conceived with the idea that agents may not have access to environmental information, directly update their state or communicate with others. We stress the relationship between agents and the environment, promoting the latter to a ``first-class citizen" and giving a central role in defining the global system.
This companion page is divided as follows:
The YODA specification language allows the designer to model agent-based systems. To help the reader, we include a brief snippet of a running example based on a robotic scenario after the corresponding general syntactic declaration.
Each YODA model may consist of a sequence of elements declaring:
- Constants and Parameters;
- Functions;
- Agents;
- Scene Elements;
- Environment;
- Configuration;
- Measures and Predicates.
Constants and Parameters permit associating names with values that can be reused in the other parts of the model.
Syntax of constants and parameters declarations is the following:
param <name> = <expr> ;
const <name> = <expr> ;
Both require the proper keyword. The syntactic category <name>
refers to names, while <expr>
is for the expressions.
Parameters are values that can be changed even after the model has been loaded. In the Robotic model, we have:
param no = 5;
param na = 3;
where no
is the number of obstacles that will be placed in the environment, and na
is the number of agents that will operate in the environment.
Constants are used to associate specific values to names, and can not be changed when the model is loaded. In the Robotic model, we have:
const height = 10;
const width = 10;
where height
and width
are respectively the height and the width of the grid (both set to 10).
Functions are used to reduce the lineof codes and to modularise the code.
The syntax is the following:
let <name> (<type> <name>, ... , <type> <name>) =
<functionStatement >
end
A function has a name and a (possibly empty) list of arguments. Following a functional approach, the body of a function can be a return
, an if then else
, or a let
in statement.
In the Robotic Scenario, we can imagine the following function:
let distance(real x1, real y1, real x2, real y2) =
let dx = x1-x2
and dy = y1-y2
in
return sqrt(pow(dx,2)+pow(dy,2))
end
This function allows to calculate the distance value between two coordinates.
The agent declaration is the main component of a YODA model and is declared in the following manner:
agent <agentName> =
<conditionsDeclaration>
<featuresDeclaration>
<observationsDeclaration>
<actionsDeclaration>
<behaviourDeclaration>
end
Now we see in detail each declaration of the agent.
The state represents the inner knowledge of the agent. The state is fully under the control of the agent and can be modified by it. This construct is declared as follows:
conditions:
(<type> <name> = <expr>;)*
The component is preceded by the keyword conditions
and we declare in the body the type, the variable name and the default value.
In the Robotic Scenario, the agent state can be declared as follows:
state:
int dirx = 0; int diry = 0;
In this example, we can highlight the direction of the agent on both axes.
The features are used to declare the external information of an agent, meaning that are not under its control, differently from the state. The syntax is the same as the state declaration, but the keyword features
is used.
A robot may consider as its features the position it has inside the environment, as follows:
features:
int x = 0; int y = 0;
Observations refer to the capacity of the agents to sense their surroundings. The syntax is the same as the state declaration, but the keyword observations
is used.
In the Robotic Scenario, the robot can observe its closer cells and can recognise if it has reached the goal area. The declaration can be the following:
observations:
bool north = false; bool south = false;
bool east = false; bool west = false;
bool goal = false;
Actions are used to allow agents to step into the next state. In this construct, we need to declare how the state fields are modified when a certain action is performed. Each action should be declared in the following manner:
<actionName> [(<name> <- <expr>;)*]
The <actionName>
is the identifier of the action that will be called later in the behaviour. The <name>
corresponds to one of the available fields in the conditions, which should be assigned with an <expr>
of the proper type.
Robots may act as declared below:
actions :
moveNorth [ dirx <- 0; diry <- 1;]
moveSouth [ dirx <- 0; diry <- -1;]
moveEast [ dirx <- 1; diry <- 0;]
moveWest [ dirx <- -1; diry <- 0;]
stop [ dirx <- 0; diry <- 0; ]
Considering the grid spaces as integer values, the agent is allowed only to move between cells through the axes. The directions are set according to the chosen movement, while they are set to 0 if the agent stops.
The behaviour is the core of the agent and is composed of a set of conditions returning a certain action. In this construct, we can consider both observation and state fields because these are intrinsically known by the agent. The syntax can be declared in the following manner:
when <guardExpr> -> [ (<actionName> : <weightExpr>;)* ]
(orwhen <guardExpr> -> [ (<actionName> : <weightExpr>;)* ])*
otherwise [ (<actionName> : <weightExpr>;)* ]
The rules must have one of the three keywords at the beginning (when
if it is the first rule, orwhen
from the second rule, and otherwise
the default one). Then, apart from the default rule, a boolean condition guardExpr
has to be declared. Finally, between the square brackets, an existing action, along with a weight, must be pointed.
In the Robotic scenario, the behaviour of a robot can be written in the following way:
behaviour :
when goal -> [ stop: 1; ]
orwhen !north -> [ moveNorth: 1; ]
orwhen north -> [ moveEast: 0.5; moveWest: 0.5; ]
orwhen north && east -> [ moveWest: 1; ]
orwhen north && west -> [ moveEast: 1; ]
otherwise [ stop: 1; ]
In case the agent has reached the goal area, it will stop moving. The agent tries to move north until it finds an obstacle: in that case, it will try to turn left, and finally turn right. If any of these movements are not possible, the default condition is applied, stopping the robot.
Elements in YODA represent those entities that are static and don't perform any operation. They can be declared as follows:
element <name> =
(<type> <name> = <expr>;)*
end
In the Robotic Scenario case, we can declare an Obstacle as an element that has both positions x and y. The initial value is set to 0.
element Obstacle =
int posx = 0; int posy = 0;
end
In a YODA environment, we can identify two main components: the sensing function and the dynamic evolution.
environment :
sensing :
(<agentName>[<sensingFunction>])*
dynamic :
(<agentName>[<dynamicFunction>])*
end
In the sensing function, we update each observation of each agent previously declared using the following syntax:
<observationName> <- <expr>;
The <observationName>
contains a variable that has been already declared in the agent observations set, while the <expr>
is a value belonging to the same type of the corresponding observation.
In this context, we can take advantage of group expressions. These allow us to perform calculations on a group of agents that fall under certain guards.
Given the previous robot specification, we can write the respective sensing function in the following way:
sensing :
Robot [
north <- any Obstacle : (posy==it.y+1)&&(posx==it.x);
south <- any Obstacle : (posy==it.y-1)&&(posx==it.x);
east <- any Obstacle : (posx==it.x+1)&&(posy==it.y);
west <- any Obstacle : (posx==it.x-1)&&(posy==it.y);
goal <- it.y==height+1;
]
The cardinal observations are set to true if there is an obstacle in the position. The goal
is set to true if the robot is over the height of the grid.
Dynamics evolution refers to a function that updates the features of each agent. The syntax for each update is similar to the one seen in the sensing function.
In the case of the Robotic Scenario, a Robot can update its position:
dynamic :
Robot [
x <- x + dirx;
y <- y + diry;
]
As we can see, the x
and y
are modified considering the directions on both axes (which belong to the state).
The configuration syntax construct is used by the simulation to initialise the system. A configuration should be expressed with the following form:
configuration <confName> :
(<collectiveExpression>)*
end
To accept a configuration we need a <confName>
, which will be later called by the simulator. A <collectiveExpression>
is an expression used to initialise the entities inside the system. It may refer to both elements and agents. In this syntax construct, we can define three types of expressions: the single instantiation, the for-loop, and the if-then-else.
In the robotic scenario, we can express a simple configuration like the one below:
configuration Main :
for ox sampled distinct no time from U[0, width] do
Obstacle[ posx = ox; posy = 7; ]
endfor
Robot[ x = 9 ; y = 0 ; ]
end
To initialise the obstacles, we can use a for-loop to create 5 (parameter no
) elements. The value ox
will be repeated five times, with a distinct random value between 0 and the width of the grid.
For this example, we initialise a single agent by assigning the initial position to the Robot.
It should be noted that the other attributes of agents are not initialised, thus the values will be the default ones.
Measures are functions that collect data and analyse system behaviour. A predicate is a boolean expression that can be evaluated either at a global or local level.
The syntax for both categories is the following:
measure <name> = <expr>
(local)? predicate <name> = <expr>
In the robotic scenario, these two functions can be used to define the following examples:
measure inGoalArea = #Robot[it.goal];
predicate success = %Robot[it.goal] == 1;
Once we defined our model, we can simulate it using Sibilla functionalities.
Before using Sibilla simulation features, the user should install it by following the guide on the following link.
To start simulating YODA scenarios, we need first a trace specification.
A trace specification is a simple text file where we can declare which tracing variable should be associated with those from the specificied model.
The trace variables are:
- x, representing the x-axis;
- y, representing the y-axis;
- z, representing the z-axis;
- direction, representing the angle (in Sequit is based on the y-axis);
- shape, representing a visualisation via an image or a model;
- colour, representing different states.
In the case of the robotic scenario, a trace specification example is the following:
shape = square;
colour = {
when goal : green;
otherwise : red;
}
Here, we assigned only the shape, which will be always a square, and the colour, which will turn green when the goal agent observation variable is set to true. The other trace variable are not declared: in this case we assume that the simulation takes the variable in the model specified with same name, or the default value is always assigned.
Sibilla is equipped with a native shell that can be used to interact with the simulation tool. The How-to-start guide, with the full commands list, is available at the following link.
To simulate the robotic scenario, we can use the following list of commands:
module "yoda"
load "robotic.yoda"
init "Main"
deadline 100
trace "robotic.trc" in "./results" h = true
The command module
identifies the type of language we are dealing with. With load
, we decide which is the file that should be loaded. The command init
detect the initial configuration inside the specification. The deadline
sets the duration of the simulation. Finally, the trace
command start a single simulation run, using the specified trace file. This last command produces a .csv file for each loaded agent and the user can decide to attach the header or not.
In this section, we can find different examples that show how YODA models work. It should be noted that all the examples are available on Google Colab notebooks.
The available case studies are:
- Robotic Scenario: a model where robots have to reach a goal area while avoiding obstacles.
- Flock: a model representing the behaviour of a flock.
- FinderBot: a model composed of a group of finders looking for a target.
- SEIR: a model based on an epidemic scenario.
- Red-Blue: a model about consensus protocols.
In our Robotic Scenario, a robot (the agent) has to reach a goal area located on the opposite side of its starting row.
The robot moves inside a grid environment while avoiding obstacles randomly distributed in the area.
In this case, the robot has a non-deterministic behaviour and when an obstacle is met, it may equally decide to go to its right or left. Otherwise, the robot will keep moving forward.
The example is available at the following link.
The model represents the behaviour of a Flock, or a group of birds moving around without getting dispersed.
This behaviour has been originally described by Reynolds in "Flocks, herds and schools: A distributed behavioral model".
Our version slightly differs from the original one, even if the main three policies (alignment, cohesion, and separation) are preserved. In fact, our model is based on a mean point extracted from the position of all the birds.
The example is available at the following link.
The model represents the behaviour of a group of searcher looking for a target close in the nearby.
The model takes inspiration from scenarios like rescuers looking for an injured person or robot gardeners searching for an invasive grass.
In the example, we allow the finders to move randomly until they find the target. In that case, they need to reach the mean position of all the finders that have reached the objective.
The example is available at the following link.
The model represents an epidemic scenario.
The agents roam in a certain area and initially are divided in two categories: susceptible and infected. The latter can infect the others.
In this model, we consider three distinct states: healthy, exposed, and infected. The agent can cycle through these three states until the end of the simulation.
The example is available at the following link.
The model represents a scenario where agents can be part of a red party or blue one.
This example is used to mimic consensus dynamics, which can refer to different algorithms and formalisms.
Our scenario consider only two states: red and blue. The agent can assume only one of the two at any time and they change side if there are more agents of its same party.
The example is available at the following link.