Implementation of Schelling’s Segregation Model

This notebook contains an implementation of Schelling’s segregation model in mesa. See 01-schelling.ipynb for an overview of the model.

Imports

from mesa import Model, Agent
from mesa.time import RandomActivation
from mesa.space import SingleGrid
from mesa.datacollection import DataCollector
import random
import matplotlib.pyplot as plt
from IPython.display import clear_output
from ipywidgets import widgets, interact, interact_manual
import seaborn as sns
import numpy as np
import pandas as pd

sns.set()

The agents and the model

class SchellingAgent(Agent):
    '''
    Schelling segregation agent
    '''
    def __init__(self, unique_id, pos, model, agent_type):
        '''
         Create a new Schelling agent.
         Args:
            pos: Agent initial location.
            agent_type: Indicator for the agent's type (minority=1, majority=0)
        '''
        super().__init__(unique_id, model)
        self.pos = pos
        self.type = agent_type

    def step(self):
        similar = 0
        neighbors = self.model.grid.neighbor_iter(self.pos)
        for neighbor in neighbors:
            if neighbor.type == self.type:
                similar += 1

        # If unhappy, move:
        if similar < self.model.homophily:
            self.model.grid.move_to_empty(self)
        else:
            self.model.happy += 1

class SchellingModel(Model):
    '''
    Model class for the Schelling segregation model.
    '''
    def __init__(self, height, width, density, minority_percent, homophily, seed = None):
        
        self.height = height
        self.width = width
        self.density = density
        self.minority_percent = minority_percent
        self.homophily = homophily

        self.schedule = RandomActivation(self)
        self.grid = SingleGrid(height, width, torus=True)

        self.happy = 0
        self.datacollector = DataCollector(
            {"happy": lambda m: m.happy},  # Model-level count of happy agents
            # For testing purposes, agent's individual x and y
            {"x": lambda a: a.pos[0], "y": lambda a: a.pos[1]})
        self.running = True
        # Set up agents
        agent_id = 0
        for cell in self.grid.coord_iter():
            #print(cell)
            _,x,y = cell
            if self.random.random() < self.density:
                if self.random.random() < self.minority_percent:
                    agent_type = 1
                else:
                    agent_type = 0
                agent = SchellingAgent(agent_id, (x, y), self, agent_type)
                agent_id += 1
                self.grid.position_agent(agent, x=x, y=y)
                self.schedule.add(agent)
        
    def step(self):
        '''
        Run one step of the model. If All agents are happy, halt the model.
        '''
        self.happy = 0  # Reset counter of happy agents
        self.schedule.step()
        self.datacollector.collect(self)
        if self.happy == self.schedule.get_agent_count():
            self.running = False

Visualization using Jupyter widgets

Warning

The code below uses Jupyter widgets which do not work unless you are running the notebook locally.

max_rounds = 100

def value(cell):
    if cell is None: return 0
    elif cell.type == 1: return 1
    elif cell.type == 0: return 2

def run_schelling_sim(height, width, density, minority_percent, homophily):
    fig, ax = plt.subplots()
    
    # initialize the model
    model = SchellingModel(height, width, density, minority_percent, homophily)
    num_rounds = 0
    while model.running and num_rounds < max_rounds:
        num_rounds += 1
        model.step()
        data = np.array([[value(c) for c in row] for row in model.grid.grid])
        df = pd.DataFrame(data)
        sns.heatmap(df, cbar=False, linecolor='white', cmap=['white', 'blue', 'red'])
        ax.axes.get_xaxis().set_visible(False)
        ax.axes.get_yaxis().set_visible(False)

        clear_output(wait=True)
        display(fig);
    plt.close()
    print(f"The simulation ran for {num_rounds} rounds.")
    print(f"Out of {model.schedule.get_agent_count()}, there are {model.happy} happy agents.")

interact_manual(run_schelling_sim, 
                height = widgets.IntSlider(
                    value=50,
                    min=1,
                    max=100,
                    step=1,
                    description='height:',
                    disabled=False,
                    continuous_update=False,
                    orientation='horizontal',
                    readout=True,
                    readout_format='d'
                ), 
                width = widgets.IntSlider(
                    value=50,
                    min=1,
                    max=100,
                    step=1,
                    description='width:',
                    disabled=False,
                    continuous_update=False,
                    orientation='horizontal',
                    readout=True,
                    readout_format='d'
                ),  
                density = widgets.FloatSlider(
                    value=0.7,
                    min=0,
                    max=1.0,
                    step=0.1,
                    description='density:',
                    disabled=False,
                    continuous_update=False,
                    orientation='horizontal',
                    readout=True,
                    readout_format='.1f'
                ),
                minority_percent = widgets.FloatSlider(
                    value=0.2,
                    min=0,
                    max=1.0,
                    step=0.1,
                    description='minority %:',
                    disabled=False,
                    continuous_update=False,
                    orientation='horizontal',
                    readout=True,
                    readout_format='.1f'
                ), 
                homophily = widgets.IntSlider(
                    value=4,
                    min=0,
                    max=8,
                    step=1,
                    description='homophily:',
                    disabled=False,
                    continuous_update=False,
                    orientation='horizontal',
                    readout=True,
                    readout_format='d'
                ), 
               );