Search code examples
simulationanylogicagent-based-modeling

How can I have different initial state for one agent type?


I'm working on an epidemic model. In this model, there are 2 types of susceptible states, S1 and S2. At the start of the model, I want there to be agents in S1 state as well as S2 state. For example, I have total 1000 agents at first, and I want there to be 200 in S1 and 800 in S2.

I tried to set S1 as the start state and create a message transition from S1 to S2, then coded in main as:

for(int i = 0; i < 1000*0.8; i++)
    send("s1 to s2", people.random());

But it will cause repeat message to the same agent, thus there won't be 800 in S2 state. Also I think it's not a good design.


Solution

  • You need some code to do selection-without-replacement so you don't choose the same agent multiple times. There's nothing built-in to AnyLogic to do this for you, so you'd want code something like the below (assuming your agents are of type Person).

    // Create selection list with all Person agents in
    List<Person> selectionList = new ArrayList<Person>();
    for (Person p : people) {
       selectionList.add(p);
    }
    
    // Select one 800 times (removing them from our selection list as we go)
    for (int i = 0; i < 800; i++) {
       int randomIndex = uniform_discr(0, selectionList.size() - 1);
       send ("s1 to s2", selectionList.get(randomIndex));
       selectionList.remove(randomIndex);
    }
    
    

    The crucial thing to remember (and something that many AnyLogic modellers don't understand) is that messages to statecharts are processed asynchronously (in hidden timeout 0 events which you can see on the Events View at runtime) however you send them (e.g., whether you use send, receive or the statechart's fireEvent function --- these just affect the number of such intermediate events and whether the "On message received" action of the Connections element is triggered or not).

    Thus an agent will never have changed state in a block of code which sends messages to them. (It will only do so after this block of code --- and the current event it is triggered from --- completes.)

    P.S. As per your comment to Emile's (incorrect) answer, the other way to do this is via dynamic events so that each message is sent after previous ones have been processed. But then you have to be very careful that you get the at-the-same-sim-time event ordering correct (loads of subtle detail here) and you'd still have to do the filtering in Emile's answer which is very wasteful; much easier to do it the more conceptually correct way where you whittle down the set of agents you're sampling from as you go.