In this article I demonstrate agent-based simulation in Python. Actually, I already introduced agent-based modeling in Python in previous posts. I e.g. introduced a battlefield model that contains agent groups that were located on a 2D grid. I coded that model in Python and used Matplotlib for visualization. In this article I will not only build a model but actually execute an entire agent-based simulation in Python.
Simple module for agent-based simulation in Python
To begin with I need to construct a simple module for agent-based simulations in Python. Firstly, much like in my previous introduction to agent-based modeling in Python, I define a custom agent datatype. This is done on the form of a custom class in Python.
# class, defining agents as abstract data types
class agent:
# init-method, the constructor method for agents
def __init__(self,x,y,group):
self.life = 100 # agent's life score
self.x = x
self.y = y
self.group = group
As can be seen from the constructor of the agent class an agent has a life score. Agents furthermore belong to a group as well as they have x- and y-coordinates. These coordinate attributes are used to describe the location of agents in a 2D grid.
Basically, a 2-dimensional array is used to contain all agents. Hence, populations of agents are allocated on a 2D grid. In this case this grid represents a battlefield. As can be seen below I create the battlefield with a list comprehension in Python.
# creating empty 100 x 100 list using list comprehension in python
battlefield = [[None for i in range(0,100)] for i in range(0,100)]
I now proceed by defining a helper function in Python for creating and allocating agents on the battlefield. I will use this function in order to setup my agent-based simulation in Python.
# define a function for creating agents and assigning them to grid
def agentCreator(size,group,groupList,field,n,m):
# loop through entire group, i.e. in this case 1000 units
for j in range(0,size):
# select random available location
while True:
# random x coordinate
x = random.choice(range(0,n))
# random y coordinate
y = random.choice(range(0,m))
# check if spot is available; if not then re-iterate
if field[x][y] == None:
field[x][y] = agent(x=x,y=y,group=group)
# append agent object reference to group list
groupList.append(field[x][y])
# exit while loop; spot on field is taken
break
In short the agentCreator-function creates a specified amount of agents and allocates them on a field. Each agent created by one function call have the same type. As can be seen from the function definition the type is a function input parameter. Another key point to notice is that the field in which agents are allocated is passed on as a reference. I.e. in this case the field is the battlefield grid. It facilitates the canvas on which this agent-based simulation in Python takes place.
Initializing my agent-based simulation in Python
Using these model components I now initiate my model. To start my agent-based simulation in Python I create an initial battlefield. I furthermore add agents to it. All this is done by calling the agentCreator function as I have shown above. I call the function twice to add two groups of agents. The first group is type “A” while the second group is of type “B”.
# list with available x and y locations
locations = battlefield.copy() # using .copy prevents copying by reference
# create empty list for containing agent references in future, type A & B
agents_A = []
agents_B = []
# assigning random spots to agents of group A and B;
import random
agentCreator(size = 1000,
group = "A",
groupList = agents_A,
field = battlefield,
n = 100,
m = 100)
agentCreator(size = 1000,
group = "B",
groupList = agents_B,
field = battlefield,
n = 100,
m = 100)
While the data will now reflect a 2D grid with two competing agent populations I cannot see it visually. I will use Matplotlib to visualize this agent-based simulation in Python. I the following paragraphs I demonstrate how to do this.
Matplotlib visualizes agent-based simulation in Python
As shown by me in one of my previous blog posts Matplotlib can be used to visualize 2D grids in Python. I have placed a reference in the form of a link at the end of this article. Make sure to read in case you want to implement this kind of visualization from scratch on your own.
In the code displayed below I use Matplotlib to visualize every cell in the battlefield grid. I do so by creating 2D float array using a list comprehension in Python. I then iterate through every index-pair of the 2D grid. Due to every index-pair in the float array matching a index-pair in the battlefield grid I can identify cells with agents in them. I overwrite cells that contain an agent. In short, every cell with an agent of type A receives the float 1.0 while every cell with an agent of type B receives the float 2.0.
#.imshow() needs a matrix with float elements;
population = [[0.0 for i in range(0,100)] for i in range(0,100)]
# if agent is of type A, put a 1.0, if of type B, pyt a 2.0
for i in range(1,100):
for j in range(1,100):
if battlefield[i][j] == None: # empty
pass # leave 0.0 in population cell
elif battlefield[i][j].group == "A": # group A agents
population[i][j] = 1.0 # 1.0 means "A"
else: # group B agents
population[i][j] = 2.0 # 2.0 means "B"
I can not visualize the battlefiend. In order to do so I import pyplot and colors from Matplotlib in Python. This enables me to create a ListedColormap object. ListedColormap objects can be bassed on as references to the pyplot.imshow()-method. I show this in the code below.
# import pyplot and colors from matplotlib
from matplotlib import pyplot, colors
# using colors from matplotlib, define a color map
colormap = colors.ListedColormap(["lightgrey","green","blue"])
# define figure size using pyplot
pyplot.figure(figsize = (12,12))
# using pyplot add a title
pyplot.title("battlefield before simulation run (green = A, blue = B)",
fontsize = 24)
# using pyplot add x and y labels
pyplot.xlabel("x coordinates", fontsize = 20)
pyplot.ylabel("y coordinates", fontsize = 20)
# adjust x and y axis ticks, using pyplot
pyplot.xticks(fontsize = 16)
pyplot.yticks(fontsize = 16)
# use .imshow() method from pyplot to visualize agent locations
pyplot.imshow(X = population,
cmap = colormap)
<matplotlib.image.AxesImage at 0x22495922ac8>
We can now execute and visualize an agent-based simulation in Python. While I only show one iteration of the simulation in this article I did place a linked reference to another article in which I use execute an entire simulation experiment.
Implementing strategies for agent-interaction
For this we implement two attack strategies:
Group A has the strategy of always hitting the same agent in each round
Group B has a random and independent strategy for attacking enemies. This means that each agent of type B will attack a randomly selected enemy within that agent’s reach.
The simulation is now conducted under the following conditions:
1) Each round is one iteration
2) In each round, each agent can attack one agent within his reach
3) The reach of an agent is defined at the start of the simulation and defaults to 10
4) If an agent dies he will no longer be located on the battle field
5) An agent dies when his life score equals or goes below zero
6) Each agent has a randomly distributed attack damage, ranging from 10 to 60
7) In each round all agents get to launch an attack
With these rules in place I will now iterate through 50 rounds of fighting. At the end I will print a plot of the remaining agents on the battlefield. The implementation follows below:
for counter in range(0,50): # in this case I am conducting 50 iterations # iterating through all cells on the battlefield for i in range(0,len(battlefield)): for j in range(0,len(battlefield)): #print("top tier iteration, i: "+str(i)+", j: "+str(j)) # check if there is an agent in the respective cell if battlefield[i][j] != None: # depending on the type: execute respective attack strategy if battlefield[i][j].group == "A": found_i = None found_j = None # look in neigbouring cells in same order for each iteration for k in range(i-10,i+11): for l in range(j-10,j+11): # check for negative index values; if so - break! if k < 0 or l < 0: break # check for index values above 99, if so break! if k > 99 or l > 99: break if battlefield[k][l]: if battlefield[k][l].group == "B": # then this is an enemy if found_i == None: found_i = k found_j = l # deal damage to identified enemy if found_i: battlefield[found_i][found_j].life = battlefield[found_i][found_j].life - random.randint(10,60) else: # first check if there even is an enemy in one of the surrounding cells enemy_found = False for k in range(i-10,i+11): for l in range(j-10,j+11): # check for negative index, if so break to next iteration if k < 0 or l < 0: break # check for index values above 99, if so break if k > 99 or l > 99: break if battlefield[k][l] != None: if battlefield[k][l].group == "A": enemy_found = True # select a random row and a random column found_i = None found_j = None while enemy_found and found_i == None: k = random.randint(i-10,i+10) l = random.randint(j-10,j+10) # check for negative index, if so continue to next iteration if k < 0 or l < 0: continue # check for index value > 99, if so continue if k > 99 or l > 99: continue if k != i: if battlefield[k][l]: if battlefield[k][l].group == "A": found_i = k found_j = l else: if l != j: if battlefield[k][l]: if battlefield[k][l].group == "A": found_i = k found_j = l # deal damage to identified enemy if found_i: battlefield[found_i][found_j].life = battlefield[found_i][found_j].life - random.randint(10,60) # identifying agents with life score of score or below - and removing them from the grid for i in range(0,len(battlefield)): for j in range(0,len(battlefield)): if battlefield[i][j]: if battlefield[i][j].life <= 0: battlefield[i][j] = None # producing a plot of all battlefield locations, 10 iterations after population = [[0.0 for i in range(0,100)] for i in range(0,100)] # if agent is of type A, put a 1.0, if of type B, pyt a 2.0 for i in range(1,100): for j in range(1,100): if battlefield[i][j] == None: # empty pass # leave 0.0 in population cell elif battlefield[i][j].group == "A": # group A agents population[i][j] = 1.0 # 1.0 means "A" else: # group B agents population[i][j] = 2.0 # 2.0 means "B" # import pyplot and colors from matplotlib from matplotlib import pyplot, colors # using colors from matplotlib, define a color map colormap = colors.ListedColormap(["lightgrey","green","blue"]) # define figure size using pyplot pyplot.figure(figsize = (12,12)) # using pyplot add a title pyplot.title("battlefield after 50 iterations (green = A, blue = B)", fontsize = 24) # using pyplot add x and y labels pyplot.xlabel("x coordinates", fontsize = 20) pyplot.ylabel("y coordinates", fontsize = 20) # adjust x and y axis ticks, using pyplot pyplot.xticks(fontsize = 16) pyplot.yticks(fontsize = 16) # use .imshow() method from pyplot to visualize agent locations pyplot.imshow(X = population, cmap = colormap)
<matplotlib.image.AxesImage at 0x22495be1848>
Concluding remarks and references to related content
In some following posts I will clean the code and pack its functionality into re-usable functions. I will then conduct a more comprehensive simulation study in which various parameters are varied to investigate their impact on battle outcome.
In this example I used Python lists as my agent containers of choice. However, similar models can be constructed using e.g. NumPy arrays.
If you are interested in agent-based simulation you might be interested in the following content:
- Link: Developing a simple agent-based simulation model in Python
- Link: Simulation methods for SCM analysts
- Link: Agent-based modeling in Python
- Link: Visualizing 2D grid with Matplotlib in Python
- Link: Agent-based simulation of electricity markets
Data scientist focusing on simulation, optimization and modeling in R, SQL, VBA and Python
Leave a Reply