CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-mesa

Agent-based modeling (ABM) in Python framework with spatial grids, agent schedulers, data collection tools, and browser-based visualization capabilities

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

core.mddocs/

Core Agent-Based Modeling

The core of Mesa consists of three fundamental classes that form the backbone of any agent-based model: Agent, AgentSet, and Model. These classes provide the essential infrastructure for creating, managing, and running agent-based simulations.

Imports

from mesa import Agent, Model
# AgentSet is accessible via Agent.create_agents() or Model.agents
from typing import Any, Callable, Iterable, Iterator, Sequence
from random import Random
import numpy as np

# Type aliases
SeedLike = int | np.integer | Sequence[int] | np.random.SeedSequence
RNGLike = np.random.Generator | np.random.BitGenerator

Agent Class

The Agent class serves as the base class for all agents in a Mesa model. It provides essential functionality for agent lifecycle management, random number generation, and integration with the model framework.

class Agent:
    """
    Base class for model agents with lifecycle management and random number generation.
    
    Attributes:
        model: Reference to the model instance containing this agent
        unique_id: Unique identifier for the agent within the model
        pos: Position of the agent in space (if applicable)
    """
    
    def __init__(self, model: Model, *args, **kwargs) -> None:
        """
        Initialize a new agent.
        
        Parameters:
            model: The model instance this agent belongs to
            *args: Additional positional arguments
            **kwargs: Additional keyword arguments
        """
        ...
    
    def step(self) -> None:
        """
        Execute one step of agent behavior.
        
        This method should be implemented by subclasses to define
        the agent's behavior during each simulation step.
        """
        ...
    
    def advance(self) -> None:
        """
        Execute the advance phase of agent behavior.
        
        Used in staged activation where agents first step(),
        then advance() to update their state based on all
        agents' step() results.
        """
        ...
    
    def remove(self) -> None:
        """
        Remove this agent from the model.
        
        This properly deregisters the agent from the model
        and handles cleanup of spatial positioning if applicable.
        """
        ...
    
    @classmethod
    def create_agents(cls, model: Model, n: int, *args, **kwargs) -> AgentSet[Agent]:
        """
        Create multiple agents of this class.
        
        Parameters:
            model: The model instance
            n: Number of agents to create
            *args: Additional positional arguments for agent initialization
            **kwargs: Additional keyword arguments for agent initialization
        
        Returns:
            AgentSet containing the newly created agents
        """
        ...
    
    @property
    def random(self) -> Random:
        """
        Access to the model's seeded Python random number generator.
        
        Returns:
            The model's Random instance for reproducible randomness
        """
        ...
    
    @property
    def rng(self) -> np.random.Generator:
        """
        Access to the model's seeded NumPy random number generator.
        
        Returns:
            The model's numpy.random.Generator instance
        """
        ...

Agent Usage Examples

from mesa import Agent, Model

class Predator(Agent):
    """Example predator agent with energy-based behavior."""
    
    def __init__(self, model, energy=100):
        super().__init__(model)
        self.energy = energy
        self.max_energy = energy * 2
    
    def step(self):
        """Hunt for prey and consume energy."""
        # Move randomly
        self.move_randomly()
        
        # Hunt for prey
        prey_in_cell = [agent for agent in self.model.space.get_cell_list_contents([self.pos])
                       if isinstance(agent, Prey)]
        
        if prey_in_cell and self.energy < self.max_energy:
            prey = self.random.choice(prey_in_cell)
            prey.remove()
            self.energy += 50
        
        # Consume energy
        self.energy -= 2
        
        # Die if energy is depleted
        if self.energy <= 0:
            self.remove()
    
    def move_randomly(self):
        """Move to a random neighboring cell."""
        if hasattr(self.model, 'space'):
            neighbors = self.model.space.get_neighborhood(self.pos)
            new_pos = self.random.choice(neighbors)
            self.model.space.move_agent(self, new_pos)

# Creating agents
model = Model()
predator = Predator(model, energy=150)

# Creating multiple agents
predators = Predator.create_agents(model, n=10, energy=100)

AgentSet Class

AgentSet is a powerful collection class for managing groups of agents. It provides efficient operations for filtering, sorting, and executing methods across multiple agents.

class AgentSet(MutableSet, Sequence):
    """
    Collection class for managing ordered sets of agents with advanced operations.
    
    AgentSet implements both MutableSet and Sequence interfaces, providing
    set-like operations while maintaining agent ordering.
    """
    
    def __init__(self, agents: Iterable[Agent], random: Random | None = None):
        """
        Initialize an AgentSet with agents.
        
        Parameters:
            agents: Iterable of agents to include in the set
            random: Random number generator for shuffle operations
        """
        ...
    
    def __len__(self) -> int:
        """Return the number of agents in the set."""
        ...
    
    def __iter__(self) -> Iterator[Agent]:
        """Iterate over agents in the set."""
        ...
    
    def __contains__(self, agent: Agent) -> bool:
        """Check if an agent is in the set."""
        ...
    
    def __getitem__(self, item: int | slice) -> Agent:
        """Get agent by index or slice."""
        ...
    
    def select(self, 
               filter_func: Callable[[Agent], bool] | None = None,
               at_most: int | float = float("inf"),
               inplace: bool = False,
               agent_type: type[Agent] | None = None) -> AgentSet:
        """
        Select agents based on criteria.
        
        Parameters:
            filter_func: Function that returns True for agents to include
            at_most: Maximum number of agents to select
            inplace: Whether to modify this AgentSet or return a new one
            agent_type: Filter by agent type/class
        
        Returns:
            AgentSet containing selected agents (or self if inplace=True)
        """
        ...
    
    def shuffle(self, inplace: bool = False) -> AgentSet:
        """
        Shuffle the order of agents.
        
        Parameters:
            inplace: Whether to modify this AgentSet or return a new one
        
        Returns:
            Shuffled AgentSet (or self if inplace=True)
        """
        ...
    
    def sort(self, 
             key: Callable[[Agent], Any] | str,
             ascending: bool = False,
             inplace: bool = False) -> AgentSet:
        """
        Sort agents by a key function or attribute name.
        
        Parameters:
            key: Function to generate sort key or attribute name
            ascending: Whether to sort in ascending order
            inplace: Whether to modify this AgentSet or return a new one
        
        Returns:
            Sorted AgentSet (or self if inplace=True)
        """
        ...
    
    def do(self, method: str | Callable, *args, **kwargs) -> AgentSet:
        """
        Execute a method on all agents in the set.
        
        Parameters:
            method: Method name (string) or callable to execute
            *args: Arguments to pass to the method
            **kwargs: Keyword arguments to pass to the method
        
        Returns:
            This AgentSet for method chaining
        """
        ...
    
    def shuffle_do(self, method: str | Callable, *args, **kwargs) -> AgentSet:
        """
        Shuffle agents and then execute a method on all agents.
        
        Parameters:
            method: Method name (string) or callable to execute
            *args: Arguments to pass to the method
            **kwargs: Keyword arguments to pass to the method
        
        Returns:
            This AgentSet for method chaining
        """
        ...
    
    def map(self, method: str | Callable, *args, **kwargs) -> list[Any]:
        """
        Apply a method to all agents and return results.
        
        Parameters:
            method: Method name (string) or callable to apply
            *args: Arguments to pass to the method
            **kwargs: Keyword arguments to pass to the method
        
        Returns:
            List of results from applying method to each agent
        """
        ...
    
    def agg(self, attribute: str, func: Callable | Iterable[Callable]) -> Any | list[Any]:
        """
        Aggregate an attribute across all agents using one or more functions.
        
        Parameters:
            attribute: Name of the attribute to aggregate
            func: Aggregation function(s) like sum, mean, max, etc.
        
        Returns:
            Aggregated value(s)
        """
        ...
    
    def get(self, 
            attr_names: str | list[str], 
            handle_missing: str = "error",
            default_value: Any = None) -> list[Any] | list[list[Any]]:
        """
        Get attribute values from all agents.
        
        Parameters:
            attr_names: Attribute name(s) to retrieve
            handle_missing: How to handle missing attributes ("error", "default", "ignore")
            default_value: Default value when handle_missing="default"
        
        Returns:
            List of attribute values (or list of lists for multiple attributes)
        """
        ...
    
    def set(self, attr_name: str, value: Any) -> AgentSet:
        """
        Set an attribute value on all agents.
        
        Parameters:
            attr_name: Name of the attribute to set
            value: Value to set (can be callable for per-agent values)
        
        Returns:
            This AgentSet for method chaining
        """
        ...
    
    def add(self, agent: Agent):
        """Add an agent to the set."""
        ...
    
    def discard(self, agent: Agent):
        """Remove an agent from the set if present."""
        ...
    
    def remove(self, agent: Agent):
        """Remove an agent from the set (raises error if not present)."""
        ...
    
    def groupby(self, by: Callable | str, result_type: str = "agentset") -> GroupBy:
        """
        Group agents by a key function or attribute.
        
        Parameters:
            by: Grouping key function or attribute name
            result_type: Type of result collections ("agentset" or "list")
        
        Returns:
            GroupBy object for grouped operations
        """
        ...

AgentSet Usage Examples

from mesa import Agent, Model
# AgentSet is accessible via Agent.create_agents() or Model.agents

class WealthAgent(Agent):
    def __init__(self, model, wealth=100):
        super().__init__(model)
        self.wealth = wealth
        self.age = 0
    
    def step(self):
        self.age += 1
        # Simple wealth dynamics
        if self.wealth > 0:
            self.wealth += self.random.randint(-5, 10)

# Create model with agents
model = Model()
agents = WealthAgent.create_agents(model, n=100)

# Selection operations
wealthy_agents = model.agents.select(lambda a: a.wealth > 200)
young_agents = model.agents.select(lambda a: a.age < 10, at_most=20)

# Sorting operations  
by_wealth = model.agents.sort(key="wealth", ascending=False)
by_age = model.agents.sort(key=lambda a: a.age)

# Bulk operations
model.agents.shuffle_do("step")  # Randomly order, then call step() on each
model.agents.set("wealth", lambda a: max(0, a.wealth))  # Ensure non-negative wealth

# Aggregation operations
total_wealth = model.agents.agg("wealth", sum)
wealth_stats = model.agents.agg("wealth", [sum, len, max, min])

# Get attribute values
all_wealth = model.agents.get("wealth")
wealth_and_age = model.agents.get(["wealth", "age"])

# Grouping operations
by_wealth_class = model.agents.groupby(lambda a: "rich" if a.wealth > 100 else "poor")
rich_agents = by_wealth_class["rich"]

GroupBy Class

The GroupBy class enables grouped operations on agents, similar to SQL GROUP BY or pandas groupby functionality.

class GroupBy:
    """
    Helper class for grouped operations on agents.
    
    Provides methods to apply operations to groups of agents
    created by AgentSet.groupby().
    """
    
    def __init__(self, groups: dict[Any, list | AgentSet]):
        """
        Initialize GroupBy with groups.
        
        Parameters:
            groups: Dictionary mapping group keys to agent collections
        """
        ...
    
    def map(self, method: Callable | str, *args, **kwargs) -> dict[Any, Any]:
        """
        Apply a method to each group and return results.
        
        Parameters:
            method: Method name or callable to apply
            *args: Arguments to pass to the method
            **kwargs: Keyword arguments to pass to the method
        
        Returns:
            Dictionary mapping group keys to results
        """
        ...
    
    def do(self, method: Callable | str, *args, **kwargs) -> GroupBy:
        """
        Execute a method on each group.
        
        Parameters:
            method: Method name or callable to execute
            *args: Arguments to pass to the method
            **kwargs: Keyword arguments to pass to the method
        
        Returns:
            This GroupBy object for method chaining
        """
        ...
    
    def count(self) -> dict[Any, int]:
        """
        Count agents in each group.
        
        Returns:
            Dictionary mapping group keys to agent counts
        """
        ...
    
    def agg(self, attr_name: str, func: Callable) -> dict[Hashable, Any]:
        """
        Aggregate an attribute within each group.
        
        Parameters:
            attr_name: Name of the attribute to aggregate
            func: Aggregation function (sum, mean, max, etc.)
        
        Returns:
            Dictionary mapping group keys to aggregated values
        """
        ...

Model Class

The Model class serves as the container and coordinator for the entire simulation. It manages agents, handles random number generation, and orchestrates the simulation loop.

class Model:
    """
    Base class for agent-based models with simulation management.
    
    The Model class provides infrastructure for managing agents,
    coordinating simulation steps, and handling random number generation.
    
    Attributes:
        running: Boolean indicating if model should continue running
        steps: Number of times model.step() has been called
        random: Seeded Python random number generator
        rng: Seeded NumPy random number generator
    """
    
    def __init__(self, 
                 *args: Any,
                 seed: float | None = None,
                 rng: RNGLike | SeedLike | None = None,
                 **kwargs: Any) -> None:
        """
        Initialize a new model.
        
        Parameters:
            *args: Additional positional arguments
            seed: Random seed for reproducible results
            rng: Random number generator or seed
            **kwargs: Additional keyword arguments
        """
        ...
    
    def step(self) -> None:
        """
        Execute one step of the model.
        
        This method should be implemented by subclasses to define
        the model's behavior during each simulation step.
        """
        ...
    
    def run_model(self) -> None:
        """
        Run the model until completion.
        
        Repeatedly calls step() while self.running is True.
        """
        ...
    
    def register_agent(self, agent):
        """
        Register an agent with the model.
        
        Parameters:
            agent: Agent to register
        """
        ...
    
    def deregister_agent(self, agent):
        """
        Deregister an agent from the model.
        
        Parameters:
            agent: Agent to deregister
        """
        ...
    
    def reset_randomizer(self, seed: int | None = None) -> None:
        """
        Reset the random number generators with a new seed.
        
        Parameters:
            seed: New random seed
        """
        ...
    
    def reset_rng(self, rng: RNGLike | SeedLike | None = None) -> None:
        """
        Reset the NumPy random number generator.
        
        Parameters:
            rng: New random number generator or seed
        """
        ...
    
    def remove_all_agents(self):
        """Remove all agents from the model."""
        ...
    
    @property
    def agents(self) -> AgentSet:
        """
        Get all agents in the model.
        
        Returns:
            AgentSet containing all registered agents
        """
        ...
    
    @property
    def agent_types(self) -> list[type]:
        """
        Get all agent types present in the model.
        
        Returns:
            List of agent classes represented in the model
        """
        ...
    
    @property
    def agents_by_type(self) -> dict[type[Agent], AgentSet]:
        """
        Get agents organized by type.
        
        Returns:
            Dictionary mapping agent types to AgentSets
        """
        ...

Model Usage Examples

from mesa import Agent, Model, DataCollector
import numpy as np

class EcosystemModel(Model):
    """Example ecosystem model with predators and prey."""
    
    def __init__(self, n_predators=20, n_prey=100, width=50, height=50):
        super().__init__()
        self.width = width
        self.height = height
        
        # Create spatial environment (would typically use mesa.space or mesa.discrete_space)
        # self.space = MultiGrid(width, height, True)
        
        # Data collection
        self.datacollector = DataCollector(
            model_reporters={
                "Predators": lambda m: len(m.agents.select(agent_type=Predator)),
                "Prey": lambda m: len(m.agents.select(agent_type=Prey)),
                "Total Population": lambda m: len(m.agents)
            },
            agent_reporters={
                "Energy": "energy",
                "Position": "pos"
            }
        )
        
        # Create agents
        for i in range(n_predators):
            predator = Predator(self, energy=100)
            
        for i in range(n_prey):
            prey = Prey(self, energy=50)
        
        self.running = True
    
    def step(self):
        """Execute one step of the ecosystem simulation."""
        # Collect data before step
        self.datacollector.collect(self)
        
        # Execute agent behaviors in random order
        self.agents.shuffle_do("step")
        
        # Check if simulation should continue
        predator_count = len(self.agents.select(agent_type=Predator))
        prey_count = len(self.agents.select(agent_type=Prey))
        
        if predator_count == 0 or prey_count == 0:
            self.running = False

# Running the model
model = EcosystemModel(n_predators=10, n_prey=50)

# Run for specific number of steps
for step in range(100):
    model.step()
    if not model.running:
        print(f"Simulation ended at step {step}")
        break

# Or run until completion
model = EcosystemModel(n_predators=10, n_prey=50)
model.run_model()

# Access agents by type
predators = model.agents_by_type[Predator]
prey = model.agents_by_type[Prey]

# Get model statistics
print(f"Final population: {len(model.agents)} agents")
print(f"Agent types: {model.agent_types}")

Advanced Usage Patterns

Custom Agent Behaviors

class SocialAgent(Agent):
    """Agent with social network capabilities."""
    
    def __init__(self, model, cooperation_probability=0.5):
        super().__init__(model)
        self.cooperation_prob = cooperation_probability
        self.social_network = set()
        self.reputation = 0.5
    
    def add_connection(self, other_agent):
        """Add a social connection."""
        self.social_network.add(other_agent)
        other_agent.social_network.add(self)
    
    def interact_with_neighbors(self):
        """Interact with agents in social network."""
        for neighbor in self.social_network:
            if self.random.random() < self.cooperation_prob:
                # Cooperate
                neighbor.reputation += 0.1
                self.reputation += 0.05
            else:
                # Defect
                neighbor.reputation -= 0.05
    
    def update_cooperation(self):
        """Update cooperation probability based on reputation."""
        self.cooperation_prob = min(1.0, max(0.0, self.reputation))
    
    def step(self):
        self.interact_with_neighbors()
        self.update_cooperation()

Staged Activation Pattern

class StagedModel(Model):
    """Model using staged activation for simultaneous updates."""
    
    def __init__(self, n_agents=100):
        super().__init__()
        
        # Create agents
        for i in range(n_agents):
            agent = SocialAgent(self)
        
        self.running = True
    
    def step(self):
        """Execute staged activation: step, then advance."""
        # Stage 1: All agents observe and plan
        self.agents.do("step")
        
        # Stage 2: All agents update their state simultaneously
        self.agents.do("advance")

Type Definitions

# Type aliases used in Mesa core modules
from typing import Any, Callable, Hashable, Iterable, Iterator, MutableSet, Sequence
from random import Random
import numpy as np

# Random number generation types
SeedLike = int | np.integer | Sequence[int] | np.random.SeedSequence
RNGLike = np.random.Generator | np.random.BitGenerator

# Agent and model types (forward references for documentation)
Agent = Agent
AgentSet = AgentSet[Agent] 
Model = Model
GroupBy = GroupBy

docs

batch-running.md

core.md

data-collection.md

experimental.md

index.md

spatial.md

tile.json