Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Mesa

Mesa is a Python-based framework for agent-based modeling (ABM). It offers built-in core components from which one can quickly setup and customize the implementation of an ABM. Furthermore, Mesa has built-in tools for data collection and analysis of the results obtained from the ABM simulations.

import mesa

Mesa contains three categories of modules:

  1. Modeling: Modules used to build the models themselves: a model and agent classes, a scheduler to determine the sequence in which the agents act, and space for them to move around on.

  2. Analysis: Tools to collect data generated from your model, or to run it multiple times with different parameter values.

  3. Visualization: Classes to create and launch an interactive model visualization, using a server with a JavaScript interface.

Agent

First create one or more agent classes based on the mesa.Agent base class.

Agents are the individual entities that act in the model. Mesa automatically assigns to each agent that is created an integer acting as a unique_id.

The agents can have attributes and methods. One or more methods may be intended to be called when time steps are taken in the model. We here implement the step() method for that purpose.

class Predator(mesa.Agent):

    def __init__(self, model, age=0, strength=100):
        super().__init__(model)  # parent class initialization

        self.type = "predator"

        self.age = age
        self.strength = strength

    def step(self):
        self.age += 1
        self.strength -= 10
        print(f"agent: {self.unique_id}, age: {self.age}, strength: {self.strength}")
class Prey(mesa.Agent):

    def __init__(self, model, age=0, food_value=50):
        super().__init__(model)  # parent class initialization

        self.type = "prey"

        self.age = age
        self.food_value = food_value

    def step(self):
        self.age += 1
        self.food_value += 1
        print(
            f"agent: {self.unique_id}, age: {self.age}, food value: {self.food_value}"
        )

Model

Next, create the model. This gives us the two basic classes of any ABM namely the agent class (population of agent objects that doing something) and the manager class (a model object that manages the creation, activation, data collection etc of the agents).

The model can be visualized as a list containing all the agents. The model creates, holds and manages all the agent objects, specifically in a dictionary. The model activates agents in discrete time steps.

A model is created as a derived class that extends mesa.Model. Agents can be added to the model upon its creation by doing so in the __init__() function. Here we instead implement specific functions for adding agents after the creation of the model.

The step() method of the model class handles the time propagation carried out with the model.agents.shuffle_do() method. The name of the step() method of the model cannot be changed.

class EcoSystem(mesa.Model):

    def __init__(self):
        super().__init__()  # parent class initialization

    def add_predators(self, number_predators=1):
        Predator.create_agents(self, n=number_predators)

    def add_prey(self, number_prey=1):
        Prey.create_agents(self, n=number_prey)

    def step(self):
        print("model step")
        self.agents.shuffle_do("step")

Time propagation

Create an instance of the model.

my_model = EcoSystem()

Add agents to the ABM.

my_model.add_predators(1)
my_model.add_prey(3)

Time propagate the ABM a certain number of discrete time steps. The shuffle_do() function will time propagate agents in random order. This is normally what you want to do, but ordered activation is also available with the do() function.

my_model.run_for(5)
model step
agent: 4, age: 1, food value: 51
agent: 2, age: 1, food value: 51
agent: 3, age: 1, food value: 51
agent: 1, age: 1, strength: 90
model step
agent: 4, age: 2, food value: 52
agent: 1, age: 2, strength: 80
agent: 3, age: 2, food value: 52
agent: 2, age: 2, food value: 52
model step
agent: 3, age: 3, food value: 53
agent: 1, age: 3, strength: 70
agent: 2, age: 3, food value: 53
agent: 4, age: 3, food value: 53
model step
agent: 3, age: 4, food value: 54
agent: 2, age: 4, food value: 54
agent: 1, age: 4, strength: 60
agent: 4, age: 4, food value: 54
model step
agent: 2, age: 5, food value: 55
agent: 3, age: 5, food value: 55
agent: 4, age: 5, food value: 55
agent: 1, age: 5, strength: 50

Adding space

Cells form the foundation of the cell space system, enabling rich spatial environments where both location properties and agent behaviors matter. They’re useful for modeling things like varying terrain, infrastructure capacity, or environmental conditions.

Cell Agents are agents that understand how to exist in and move through cell spaces. They are specialized agent classes that handle cell occupation, movement, and proper registration.

We have:

CellAgent: Mobile agents that can move between cells

FixedAgent: Immobile agents permanently fixed to cells

Grid2DMovingAgent: Agents with grid-specific movement capabilities

These classes ensure consistent agent-cell relationships and proper state management as agents move through the space. They can be used directly or as examples for creating custom cell-aware agents.

Moving cell agents

class MyAgent(mesa.discrete_space.CellAgent):

    def __init__(self, model, cell):
        super().__init__(model)

        self.move_to(cell)  # place agent in grid

    def move(self):

        new_cell = self.cell.neighborhood.select_random_cell()
        self.move_to(new_cell)

        print(f"agent: {self.unique_id}, position: {self.cell.position}")


class MyModel(mesa.Model):

    def __init__(self, number_agents, width, height):
        super().__init__()

        self.num_agents = number_agents

        self.grid = mesa.discrete_space.OrthogonalMooreGrid(
            (width, height),
            torus=False,
            random=self.random,
        )

        agents = MyAgent.create_agents(
            self,
            self.num_agents,
            # Randomly select cells for agents
            self.random.choices(self.grid.all_cells.cells, k=self.num_agents),
        )

        print(f"model time: {self.time}")
        for agent in agents:
            print(f"agent: {agent.unique_id}, position: {agent.cell.position}")

    def step(self):
        print(f"model time: {self.time}")
        self.agents.shuffle_do("move")
my_model = MyModel(3, 10, 10)
model time: 0.0
agent: 1, position: [5. 0.]
agent: 2, position: [7. 3.]
agent: 3, position: [2. 9.]
my_model.run_for(5)
model time: 1.0
agent: 2, position: [8. 2.]
agent: 3, position: [1. 8.]
agent: 1, position: [6. 0.]
model time: 2.0
agent: 1, position: [5. 0.]
agent: 2, position: [9. 2.]
agent: 3, position: [2. 8.]
model time: 3.0
agent: 1, position: [4. 0.]
agent: 3, position: [1. 9.]
agent: 2, position: [9. 3.]
model time: 4.0
agent: 3, position: [1. 8.]
agent: 2, position: [9. 4.]
agent: 1, position: [5. 1.]
model time: 5.0
agent: 1, position: [5. 0.]
agent: 2, position: [9. 3.]
agent: 3, position: [2. 9.]

In this example, we have created a model with three cell agents moving in a landscape of 10x10 grid cells. In every time step, every agent move from its present cell to a neighboring cell.