Challenge – Initializing 100 agents. Each one based on the other 99

On this challenge I will show two little tricks and things you should think about when you are developing an agent based or hybrid model. In this particular case, we want to initialize 100 agents depending on the state of the other 99. Of course there may be other ways to develop the same model, but I will take a particular road to be able to describe some errors that can be made and the tricks that can be used.

The Problem

The challenge is the following:

We must generate 100 agents and we want to initialize the state they belong to.

  • The model starts with no agents, and after 1 second, 100 agents appear at once instantaneously.
  • We know that there is a 50% chance for an agent to be sick.
  • We know that each one of the agents that are not sick received a package from one of the other 99 agents (no need to model the package)
  • If the package was sent by a sick person, then the agent who received it begins the simulation in the latent state
  • In any other case, the agent begins in the healthy state.

The agent state chart may look something like this with 3 distinct initial states.

Figure 1 – Three initial states

Try to do it yourself. If you can’t, click below to show the solution.

[read more=Show_Solution less=Hide_Solution]

A solution… with a problem.

Since the agents are created after 1 second, an event that will be triggered after 1 second has to be created with the following code (the argument in the add_myAgents function is not necessary if you define the parameter default value as randomTrue(0.5).

for(int i=0;i<100;i++){

    add_myAgents(randomTrue(0.5));

}

Figure 2 – Adding variable, parameter and function to the model

As seen in figure 2, three states are possible in this model: healthy, latent and sick. The parameter “infected” will define if the agent is infected in its creation (true or false). The variable “latentInfection” will define if the agent is in the latent state in its creation (true or false) and a function “willBeInitiallyInfected” defines the value of “latentInfection” as seen in the following code:

MyAgent lastContactedAgent = randomWhere( main.myAgents, a-> !a.equals(this) );

if(lastContactedAgent.infected) latentInfection=true;

“willBeInitiallyInfected” is a function that selects a random agent and if that agent is infected, “latentInfection” will take be true.

This function will be called in the statechart entry point:

if(!infected) willBeInitiallyInfected();

So, if the agent in question is not infected, we will verify if it’s latent according to the function “willBeInitiallyInfected”

On the branch, the agent will move to the sick state if infected is true and it will move to the latent state if latentInfection is true. In any other case it will move to the healthy state.

Download the model clicking here.

If you run the model, everything will seem fine getting the output in figure 3, but there’s a problem with the model. Can you see what that problem is?

Figure 3 – Model output, counting the number of agents in each state.

Analyze it and discover what the problem is and find a way to solve it? Try it yourself, or you can check the solution clicking “Show”.

”Show”

 

What was the problem?

If you found out the problem, well congratulations. But even if you did, there is still some value you can get from here.

It is very common when we run our models to keep the seed fixed, which is great to find problems in your models, but it’s also great to hide the problems. In the model you downloaded, the seed value is set to 5023 (figure 4).

Figure 4 – Fixed seed simulation

The problem is here:

MyAgent lastContactedAgent = randomWhere( main.myAgents, a-> !a.equals(this) );

if(lastContactedAgent.infected) latentInfection=true;

The randomWhere function will return null if there is nothing found that fulfills the condition. The first agent that is created, will return null with the randomWhere function, because at that point, it is the only agent in the population. So lastContactedAgent.infected will throw an error. Since this function is executed only if the agent is not infected, the seed value will hide this problem because it will always have a first agent that is infected and hence doesn’t execute that function.

It is important in this case to wait for all the agents to be created before checking if this agent should go to latent or not. A fixed seed, if you are unlucky, will never throw this error and you can end up building a giant model having these little mistakes. And when it’s time to do a monte carlo simulation or something of the sort, BOOM! Errors everywhere.

Tip #1: Build your simulations using random seed. Use fixed seed only for testing and debugging.

A solution

Now we understand that we need to create all the agents before checking if the agent should go to latent. To do that, we can create an auxiliary state and change the code to check from latent from the entry point to the exit action of our auxiliary state. The timeout of the auxiliary state can be 1 millisecond or less. We only need a minimal delay to ensure that all the agents are created before checking if the agent should go to latent state or not.

Figure 4 – Final Model

Download the model clicking here.

Tip #2: Auxiliary states can be very helpful in the initialization of agents. I also use dynamic events with very short timeouts to ensure that things are happening after everything else was calculated.

 

”Hide”

[/read]

All Comments:

  1. Ehsanul

    May 4, 2023

    How can i remove the 47 agents from the system that are sick?

Your Comment: