AnyLogic - custom network - anylogic

I'm still having troubles with AnyLogic...I'm developing an epidemic SIRS model and I want to define my own network.
In particular, I have this matrix that defines the daily average number of contacts between age class
and therefore I want every agent to establish contact with other agents according to this matrix...it is driving me crazy :S
AgeClass is a parameter calculated with the following function
I thought to setup an event that occurs once at the beginning with the following code
Now I am saying "connect n times to a random agent"...what I want to say is "connect n times to a random agent with AgeClass k" is there a way to do so?
thanks for the support!
ps when I write int i = AgeClass i takes the value of the parameter AgeClass of the agent that is running the code, right? So i it will be different for different agents?

Probably, you have already found a solution. Here a way to do it:
Regarding age, you don't need that big if/else if sequence. Just do something like this:
int ageClass = 0; // a variable of agents
ageClass = (int) floor(age / 5.0);
if (age >= 70.0 ) ageClass == 14; // just to be sure that max class is 14
return ageClass;
Regarding the network. I would create a function named setup, so that you can put it in agent actions, on startup, e.g. setup();
You can create a link to agents object at the agent level (Person in my code, I use a connection object named contacts). The function would be something like:
// loop through age groups
for (int i = 0; i < network[0].length; i++) {
ArrayList<Person> ageGroupPeople = new ArrayList<Person>();
for (Person p : population ) {
if ( p.ageClass == i ) { ageGroupPeople.add(p) } \\ create pool of potential alters by age
}
\\ create network per agent
for (Person ego : population ) {
for (int k = 0; k < poisson(network[ego.ageClass][i]); k++) {
Person alter = randomFrom(ageGroupPeople);
if ( ego != alter ) { ego.contacts.connectTo(alter);}
}
}
I haven't checked the code and how slow might be, it is just one way to do it.

In AnyLogic, you can represent a matrix as two-dimensional Java array:
http://help.anylogic.com/topic/com.xj.anylogic.help/html/code/Arrays.html
After initializing the matrix, you may define custom contact network using the element 'Link to agents':
http://help.anylogic.com/topic/com.xj.anylogic.help/html/agentbased/Link.html

Related

Anylogic find agent in population and access one of its variables

I want to find the index my agent has in its population (named dullieses), which lives in my Main-agent.
I want to find one of my dullieses-agent via one of its variables (DullyID) which, in this example, shall be equal to my counter variable. Both are of type int.
I have tried the following to no avail:
int i = 0;
for ( i = 0; i < dullieses.size(); i++){
if (Main.dullieses(i).DullyID == counter){
traceln("I found myself! I have the index " + i);
break;
}
}
Error code: Cannot make a static reference to the non-static method dullies(int) from type Main.
How can I find my agent in my population and find its index?
I think you're probably already in main and don't need to add Main..
Anyway, I would recommend using:
Dully d = findFirst( dullieses, d -> d.ID == counter );
d.variable = 10;
I used variable and 10 as random examples, but this should give you the idea. Also replace Dully with whatever your agent type name is.
You don't need to use a custom ID parameter in your agents (at least not for what you're doing here).
All agents in a population have the getIndex function which returns their index in the population. Just be wary that, if you remove agents from a population, their indices will change (so you can't, for example, use their index as a unique ID for them in that case).

Looking for advice on improving a custom function in AnyLogic

I'm estimating last mile delivery costs in an large urban network using by-route distances. I have over 8000 customer agents and over 100 retail store agents plotted in a GIS map using lat/long coordinates. Each customer receives deliveries from its nearest store (by route). The goal is to get two distance measures in this network for each store:
d0_bar: the average distance from a store to all of its assigned customers
d1_bar: the average distance between all customers common to a single store
I've written a startup function with a simple foreach loop to assign each customer to a store based on by-route distance (customers have a parameter, "customer.pStore" of Store type). This function also adds, in turn, each customer to the store agent's collection of customers ("store.colCusts"; it's an array list with Customer type elements).
Next, I have a function that iterates through the store agent population and calculates the two average distance measures above (d0_bar & d1_bar) and writes the results to a txt file (see code below). The code works, fortunately. However, the problem is that with such a massive dataset, the process of iterating through all customers/stores and retrieving distances via the openstreetmap.org API takes forever. It's been initializing ("Please wait...") for about 12 hours. What can I do to make this code more efficient? Or, is there a better way in AnyLogic of getting these two distance measures for each store in my network?
Thanks in advance.
//for each store, record all customers assigned to it
for (Store store : stores)
{
distancesStore.print(store.storeCode + "," + store.colCusts.size() + "," + store.colCusts.size()*(store.colCusts.size()-1)/2 + ",");
//calculates average distance from store j to customer nodes that belong to store j
double sumFirstDistByStore = 0.0;
int h = 0;
while (h < store.colCusts.size())
{
sumFirstDistByStore += store.distanceByRoute(store.colCusts.get(h));
h++;
}
distancesStore.print((sumFirstDistByStore/store.colCusts.size())/1609.34 + ",");
//calculates average of distances between all customer nodes belonging to store j
double custDistSumPerStore = 0.0;
int loopLimit = store.colCusts.size();
int i = 0;
while (i < loopLimit - 1)
{
int j = 1;
while (j < loopLimit)
{
custDistSumPerStore += store.colCusts.get(i).distanceByRoute(store.colCusts.get(j));
j++;
}
i++;
}
distancesStore.print((custDistSumPerStore/(loopLimit*(loopLimit-1)/2))/1609.34);
distancesStore.println();
}
Firstly a few simple comments:
Have you tried timing a single distanceByRoute call? E.g. can you try running store.distanceByRoute(store.colCusts.get(0)); just to see how long a single call takes on your system. Routing is generally pretty slow, but it would be good to know what the speed limit is.
The first simple change is to use java parallelism. Instead of using this:
for (Store store : stores)
{ ...
use this:
stores.parallelStream().forEach(store -> {
...
});
this will process stores entries in parallel using standard Java streams API.
It also looks like the second loop - where avg distance between customers is calculated doesn't take account of mirroring. That is to say distance a->b is equal to b->a. Hence, for example, 4 customers will require 6 calculations: 1->2, 1->3, 1->4, 2->3, 2->4, 3->4. Whereas in case of 4 customers your second while loop will perform 9 calculations: i=0, j in {1,2,3}; i=1, j in {1,2,3}; i=2, j in {1,2,3}, which seems wrong unless I am misunderstanding your intention.
Generally, for long running operations it is a good idea to include some traceln to show progress with associated timing.
Please have a look at above and post results. With more information additional performance improvements may be possible.

I want to convert from Point to Agent that gives me proper location

I am working on agent-based modelling where I have created a function that generates a random point inside a region and then converts that point to agent. However, when I use this function later, the points don't give me a proper location on the map for further computation. I want to know what I am doing wrong? Or how can this be improved.
The return type for the function is ArrayList
ArrayList<DeliveryHouse> House = new ArrayList<DeliveryHouse>();
int i;
Point p;
DeliveryHouse h;
for(i=0;i<packagesC2C;i++)
{
p = main.Region2.randomPointInside();
h = main.deliveryHouse.setLocation(p);
House.add(i, h);
};
return House;
The major problem I see with your code is that you are not initializing the agent "h" but pointing it to one agent you have at main which means you keep changing its location in the loop and it will only reflect the last location. The array list you created will have multiple references to same agent instead of one agent per location.
If you want to create an agent per location you need to create an agent population called "deliveryHouses" of the agent type "DeliveryHouse" and change your code to the following. Note that the population will have all the agents so you do not need any output from the function.
int i;
Point p;
for(i=0;i<packagesC2C;i++)
{
DeliveryHouse h = main.add_deliveryHouses();
p = main.Region2.randomPointInside();
h.setLocation(p);
}

Anylogic referencing columns in a collection

I am using a collection to represent available trucks in a system. I am using a 1 or 0 for a given index number, using a 1 to say that indexed truck is available. I am then trying to assign that index number to a customer ID. I am trying to randomly select an available truck from those listed as available. I am getting an error saying the left-hand side of an assignment must be a variable and highlighting the portion of the code reading Available_Trucks() = 1. This is the code:
agent.ID = randomWhere(Available_Trucks, Available_Trucks() = 1);
The way you are doing it won't work... randomWhere when applied to a collection of integers, will return the element of the collection (in this case 1 or 0).
So doing
randomWhere(Available_Trucks,at->at==1); //this is the right synthax
will return 1 always since that's the value of the number chosen in the collection. So what you need is to get the index of the number of the collection that is equal to 1. But you will have to create a function to do that yourself... something like this (probably not the best way but it works: agent.ID=getRandomAvailbleTruck(Available_Trucks);
And the function getRandomAvailbleTruck will take as an argument a collection (arrayList probably).. it will return -1 if there is no availble truck
int availableTrucks=count(collection,c->c==1);
if(availableTrucks==0) return -1;
int rand=uniform_discr(1,availableTrucks);
int i=0;
int j=0;
while(i<rand){
if(collection.get(j)==1){
i++;
if(i==rand){
return j;
}
}
j++;
}
return -1;
Now another idea is to instead of using 0 and 1 for the availability, you can use correlative numbers: 1,2,3,4,5 ... etc and use a 0 if it's not available. For instance if truck 3 is not availble, the array will be 1,2,0,4,5 and if it's available it will be 1,2,3,4,5.
In that case you can use
agent.ID=randomTrue(available_trucks,at->at>0);
But you will get an error if there is no available truck, so check that.
Nevertheless, what you are doing is horrible practice... And there is a much easier way to do it if you put the availability in your truck if your truck is an agent...
Then you can just do
Truck truck=randomWhere(trucks,t->t.available==1);
if(truck!=null)
agent.ID=truck.ID;

Connect different agent type Anylogic

I have 5 agents - bus, transformer , generator, load , line. I have a requirement where i need dynamically connect the above agents.
I have written the following code, where i am trying to connect for example bus [2] and generator[5]. The connections are not random but based on a circuit, which i am dynamically reading through a text file.
for (int l = 0; l < busList.size(); l++) {
for (int k = 0; k < generatorList.size(); k++) {
if (generatorList.get(k).getBusNumber()==busList.get(l).getBusNumber()) {
busList.get(l).getBusID().connectTo(generatorList.get(k).getGenID());
}
}
}
Can someone help with the connection part. I cannot have a generic list because I need the agent attributes for other functionalities as well.
Assuming that busList and generatorList are collections containing entries of type Bus and Generator respectively, which both are derived of type Agent, the following code should work for connecting:
busList.get(l).connectTo(generatorList.get(k));
I don't know why you used getBusID and getGenID which I assume return an integer. If they really return a Bus or a Generator object it should work as you already wrote. In this case you would need to provide us the error message or the exact problem that you have.