Agent
Definition of Agents
In multi-agent systems, an agent is an autonomous entity capable of perceiving its environment through sensors and acting upon that environment through actuators. Agents can range from simple rule-based systems to complex intelligent programs that learn from experience and adapt their behavior over time. They are often designed to pursue certain goals, make decisions, and interact with other agents or their environment in a meaningful way.
During a single tick, the agent undergoes a full cycle of perception, deliberation, and action. This process begins with the exploration of its environment, which gathers sensory input or data about the current state of the world. This sensory information is then interpreted and compared with the agent’s internal knowledge, including past experiences, learned models, and its current intentions or goals.
Based on this evaluation, the agent then enters a decision-making phase. Here, it analyzes possible actions within its ActionSpace, which defines the set of actions available to it at any given moment. The agent may apply heuristic rules, learned policies, or optimization methods to select the most suitable action(s) for achieving its objectives.
Finally, the chosen action(s) are executed, and the environment is influenced accordingly. This loop repeats with each tick, allowing the agent to continuously adapt and react to changes in its surroundings.
Agents in MARS
Along with layers, agents are the main part of every model. Once the layers have been created, agent types can be specified. For this step, the modeler needs to know what each agent type is going to be, what attributes define it, and what its actions will look like. Once this has been established, the agent type creation can be started.
Agents are the central part of the MARS simulation, which can be viewed as an information system with three phases. First, there is some kind of input data that is integrated in the model, either in layer or agent. The model execution is then performed and generates output data in different formats for analyzation.
A more detailed view on the system emphasises that the input files are described in the simulation config. The input data is used to initialize certain properties of the agent, which may afterwards finalize the initialization process with model code. The model execution ticks the agent in every time step. The output is created automatically by the system. Output formats as well as filter criteria need to be defined in the simulation config. To analyze the results, as for now, external tools are required.
Implementation of Agents
Agent types are defined by implementing the IAgent<TLayer>
interface. The IAgent<TLayer>
interface expects the implementation of the Init(TLayer)
and the Tick()
method as well as a unique identifier in form of a UUID (Guid) which has to be assigned by the ID
property.
The agent and entity contract is structured as follows:
In the model code a using
import for the namespace Mars.Interfaces.Agents
must be added:
using Mars.Interfaces.Agents;
An agent definition may look like this.
using Mars.Interfaces.Agents;
public class MyAgentType : IAgent<MyLayer>
{
public void Init(MyLayer layer)
{
// finalize the init process
}
public void Tick()
{
// define your logic here
}
public Guid ID { get; set; }
}
Each agent type, which shall be available for the scenario configuration have to be registered within the ModelDescription
.
Use method AddAgent<TAgentType, TLayerType>()
expects therefore the connection of the agents to its responsible layer:
// Define the description when not already happened
// var description = new ModelDescription();
// Register the agent type before adding the agent type.
description.AddLayer<MyLayerType>();
description.AddAgent<MyAgentType, MyLayerType>();
When each agent type is registered, the type and their parameter can be used within the scenario configuration.
Note that the respective layer type must be registered to the ModelDescription
beforehand.
Passing input to the Agent
To initialize agents or entities with given inputs the IAgentManager
or the IEntityManager
is used. Both components read the scenario configuration and use the mapping to create objects from it.
For example, to use the IAgentManager
, a layer (more on defining layers here) can be used to load the component that provides the IAgentManager
interface, for example inside the InitLayer()
method:
public class MyAgentLayer : AbstractLayer
{
public override bool InitLayer(TInitData layerInitData, RegisterAgent registerAgentHandle, UnregisterAgent unregisterAgentHandle)
{
base.InitLayer(layerInitData, registerAgentHandle, unregisterAgentHandle);
// Resolve the required model component.
var agentManager = layerInitData.Container.Resolve<IAgentManager>();
// Create and register objects of type MyAgentType.
var agents = agentManager.Spawn<MyAgentType>();
// Otherwise only create them but do not registering
// to trigger their Tick() method.
var nonRegisteredAgents = agentManager.Create<MyAgentType>().ToList();
return true;
}
}
The Spawn<MyAgentType>()
call creates and registers a set of agents from the configuration, unless otherwise specified in the call, and uses the mapping to assign properties of each MyAgentType
object with the desired value.
The number of agents is defined in the configuration via the
count
switch.
The Spawn<MyAgentType>()
call returns an iterator for creating and registering objects. The .NET Language Integrated Query (LINQ) technology allows subsequent queries on the set of objects before they are actually created. Only when ToList()
is iterated or called do the statements actually pass through.
Using PropertyDescription
annotation for those parameters of an agent or entity which shall be assigned with input data from outside. Properties must be publicly visible and have both a get
(getter) and set
(setter) to allow values to be written.
public class MyAgentType : IAgent<MyLayer>
{
[PropertyDescription]
public double InputProperty { get; set; }
public void Init(MyLayer layer) { }
public void Tick()
{
// do something with the input
Console.WriteLine(InputProperty);
}
public Guid ID { get; set; }
}
The PropertyDescription
is used to mark properties of a type to make this a scenario configuration (described here) externally visible.
Inside the mappings
a value or indirection mapping can be done via the name of the model element (here InputProperty
).
Direct value assignment
Values for properties can be assigned to instances of a type directly in the configuration. A direct assignment assigns the property via a name (here InputProperty
) with a desired value that conforms to the typing of the property (double
-precision value of InputProperty
).
{
"name": "MyAgentType",
"mappings":[
{"name": "InputProperty", "value": 3.141 }
]
}
The system tries to convert the input to the type automatically, so other input such as value written as "3.14"
are also possible. However, if the input does not conform to the given type, an exception is raised. For example, the InputProperty
which is typed as double
is assigned the value true
:
{
"name": "MyAgentType",
"mappings":[
{"name": "InputProperty", "value": true }
]
}
Then the system throws the following exception:
The type Double
for property InputProperty
is not compatible with input True
.
Indirect value assignment
An indirect value assignment allows to initialize each instance of an agent with its individual data objects. These data objects are stored in a file (for instance a csv-file), which is assigned to the agent type in the simulation config.
A given source (here myAgentType.csv
) contains x data objects (rows, without header row). Each data row represents one agent instance:
fieldNameInCsv | anotherField |
---|---|
0.481 | gold |
1.482 | silver |
79.13 | copper |
100.48 | platinum |
The attribute fieldNameInCsv
of the input myAgentType.csv
does not correspond to the name InputProperty
of the model object. To access the values of the field, the field name must be specified to redirect the access reading the source:
{
"name": "MyAgentType",
"file": "myAgentType.csv",
"mappings":[
{"name": "InputProperty", "field": "fieldNameInCsv" }
]
}
Alternative names
Instead of the property name from the model (here InputProperty
) a field Name
with an alternative attribute name can be specified at the PropertyDescription
.
public class MyAgentType : IAgent<MyLayer>
{
[PropertyDescription(Name = "myOtherName"]
public double InputProperty { get; set; }
}
Mapping of values can now be specified via MyInputProperty
and myOtherName
:
{
"name": "MyAgentType",
"mappings":[
{"name": "myOtherName", "value": 3.141 }
]
}
Required properties
Models that define parameters that must be externally mapped can specify this requirement as Required
in PropertyDescription
. To initialize an object, there must then be a mapping for the respective annotated properties, otherwise the system throws an exception.
public class MyAgentType : IAgent<MyLayer>
{
[PropertyDescription(Required = true)]
public double InputProperty { get; set; }
}