Last updated on 16th July 2024
Actions
and Sequences
are used to define a sequence of actions for the agents to follow at every step, or at setup.
A Sequence
is a list of actions
that are executed consecutively. Sequences are composable, which means a sequence can be a list of other sequences as well as actions. Multiple sequences can be run in a single setup()
or step()
.
Every sequence needs to be run in order for the sequence to be executed.
Creating and running an empty Sequence (Java)
public class MyClass extends AgentBasedModel<GlobalState> {
...
run(Sequence.create());
...
}
Actions have to be created for a specific class. It is best practice to create actions inside the class of the Agent the action is being defined for.
When creating an action, two parameters need to defined.
Creating actions and adding them to a sequence (Java)
public static Action<AgentA> sendMessageToAgentB =
Action.create(AgentA.class, agentA -> { /** send message */ });
public static Action<AgentB> recieveMessage =
Action.create(AgentB.class, agentB -> { /** recieve message */ });
run (
Sequence.create(
sendMessageToAgentB,
recieveMessage
)
);
The run
method can also take a list of actions which it will execute as though it's a sequence, so the above code could also be written like this.
(Java)
public static Action<AgentA> sendMessageToAgentB =
Action.create(AgentA.class, agentA -> { /** send message */ });
public static Action<AgentB> recieveMessage =
Action.create(AgentB.class, agentB -> { /** recieve message */ });
run (
sendMessageToAgentB,
recieveMessage
);
Message passing(Java)
//If AgentB isn't sent message, it will not do other processing
run (
Sequence.create(
AgentA.conditionallySendMessage(),
AgentB.recieveMessageActionAndDoOtherProcessing())
),
//Split other processing into another sequence to ensure all AgentB do processing
Sequence.create(
AgentA.conditionallySendMessage()),
AgentB.recieveMessageAction())
),
Sequence.create(
AgentB.doOtherProcessing())
)
);
Message passing (Java)
//Message will be passed from AgentA to AgentB
run (
Sequence.create(
Sequence.create(AgentA.sendMessageAction()),
Sequence.create(AgentB.recieveMessageAction())
),
//Message will NOT be passed from AgentA to AgentB
Sequence.create(
AgentA.sendMessageAction())
),
Sequence.create(
AgentB.recieveMessageAction())
)
);
In all the sequences we have shown until now, the actions happen one after the next, like in the following diagram.
It is also possible to have two actions happening at the same time, as in the diagram below. Here, AgentA can send a message that both AgentB and AgentC receive and process at the same time. They can then both send a message to AgentA to process.
Put the actions
that need to happen in parallel in a split
.
Split (Java)
run (
AgentA.doAction1(),
Split.create(
AgentB.doAction1(),
AgentC.doAction1()
),
AgentA.doAction2())
);
It is also possible to want to do several actions in parallel, or sequences of their own in parallel.
Multi-Split(Java)
run (
AgentA.doAction1(),
Split.create(
AgentB.doAction1(),
Sequence.create(
AgentC.doAction1(),
AgentB.doAction2()
)
),
AgentA.doAction2())
);
The sequence on which actions will be processed in this case is
(Java)
// nested sequence removed and created as a variable
private Sequence agentCActions =
Sequence.create(
AgentC.doAction1(),
AgentB.doAction2()
)
// main sequence is now easier to read
run (
Sequence.create(
AgentA.doAction1(),
Split.create(
AgentB.doAction1(),
agentCActions
),
AgentA.doAction2())
)
);
It is possible to compose sequences
and actions
to create the complex sequences as needed. Example of more complex sequence
- with a sequence inside a sequence
.
Complex Split (Java)
private Sequence subSequence =
Sequence.create(
AgentB.doAction2(),
Split.create(
AgentC.doAction1(),
AgentA.doAction2()
),
AgentD.doAction1())
);
run (
Sequence.create(
Split.create(
subSequence,
AgentB.doAction1()
)
)
);
In conjunction with the @ModelSettings
annotation referred to here you can specify a 'kickoff' and 'end' time. This allows you to make usage of a set of functions to manage your sequencing of actions.
run()
however this will only execute on the first tick.run()
however this will only execute on the last tick as defined by the end
annotation in @ModelSettings
run()
however this will only execute on the kickoff tick as defined by the kickoff
annotation in @ModelSettings
finish()
function has been called. Unlike the above this function is overwritten and not contained within the step
function.With this you can easily setup a model flow that will make for a more readable and understandable sequencing of actions as demonstrated below.
Complex Time Control (Java)
@Override
public void step() {
super.step();
firstStep(
AgentA.startupAction(),
AgentB.startupAction()
);
kickOff(
AgentA.kickOffAction(),
AgentB.kickOffAction()
);
run(
AgentA.sendMessageAction(),
AgentB.recieveMessageAction()
);
lastStep(
AgentA.finalAction(),
AgentB.finalAction()
);
}
@Override
public void done() {
super.done();
...
// execute Java code here
...
}
While first/last and set kickoff times are made available, you are also able to get the current tick and write any additional checks during a standard run()
action if your model has additional actions that require a specific timing.