{ "cells": [ { "cell_type": "markdown", "id": "cfb85ff0", "metadata": {}, "source": [ "# Agent-Based Modeling\n", "\n", "In this set of lectures, we'll study how to design agent-based models in Python. \n", "\n", "> An *agent-based model* (ABM) is a simulation model in which many individual entities (*agents*) interact with each other according to fixed rules. \n", "\n", "ABMs are often used for modeling a wide range of social and biological systems. In fact, you've already seen an example of an ABM: the SIR model of disease spred that we studied in the previous lecture is one. There, we relied on tools from NetworkX and various other familiar programming paradigms. We'll now explore the topic of agent-based modeling from a somewhat more systematic and flexible perspective. \n", "\n", "There exist a large number of dedicated software packages for agent-based modeling. In this course, we'll use a relatively recent package, called [Mesa](https://mesa.readthedocs.io/en/master/index.html), for agent-based modeling in Python. To install the software, run the following code in your terminal: \n", "\n", "```\n", "conda activate PIC16B\n", "conda install -c conda-forge mesa\n", "```\n", "\n", "# The Schelling Model of Racial Segregation\n", "\n", "In this set of lecture notes, we will implement the Schelling model of racial residential segregation. The Schelling model is a parable of how only *mild* individual biases can lead to highly segregated outcomes. \n", "\n", "\n", "In the Schelling Model, individuals of two types begin arranged randomly on a grid, which is often taken to represent a city. Not all grid squares are occupied. Here's an example starting configuration\n", "\n", "
\n", " \"\"\n", "
An example starting configuration in the Schelling model. Image credit: Vi Hart and Nicky Case.
\n", "
\n", "\n", "Here's how the model works: \n", "\n", "1. At each timestep, agents look at their surroundings. An agent is **unhappy** if fewer than 1/3 of their neighbors have the same type, and is **happy** otherwise. \n", "2. All **unhappy** agents pick a random empty spot and move there. All **happy** agents stay where they are. \n", "\n", "We run the model until all agents are happy. The fundamental result of the model is that, even though agents have only mild biases -- they simply prefer not to be outnumbered -- acting on their preferences can still lead to highly segregated outcomes, like this: \n", "\n", "
\n", " \"\"\n", "
An example final configuration in the Schelling model. Image credit: Vi Hart and Nicky Case.
\n", "
\n", "\n", "For an excellent interactive demonstration of the Schelling Model, check out [this blog post](https://ncase.me/polygons/) by Vi Hart and Nicky Case. \n", "\n", "### A Note on History\n", "\n", "The Schelling model does not include any concepts of historical oppression, wealth, or power, all of which contribute to racial segregation. The message of the Schelling model is that these factors are not **needed** for segregation -- mildly racist individual preferences would be enough. It is important, however, not to confuse this mathematical parable with the actual historical circumstances of racial segregation in the US or elsewhere. In most societies, including the US, racial segregation arises because of systematic oppression enforced by policy, violence, and erasure. \n", "\n", "### Sources\n", "\n", "These lecture notes are closely based on the [Schelling model example](https://github.com/projectmesa/mesa/tree/main/examples/schelling) in the [official Mesa repository](https://github.com/projectmesa/mesa). They also draw on the [Introductory Tutorial](https://mesa.readthedocs.io/en/master/tutorials/intro_tutorial.html) from the official Mesa documentation. \n", "\n", "# Implementing the Schelling Model" ] }, { "cell_type": "markdown", "id": "601ffa74", "metadata": {}, "source": [ "Let's start by implementing a bare-bones model. While there is some flexibility in how one does this, there are a few common features of most Mesa models: \n", "\n", "1. There must be an *agent* class, which should inherit from `mesa.Agent`. This class specifies the properties and behaviors of an individual agent in the simulation. \n", " - This class must call `mesa.Agent.__init__()` as part of its `__init__()` method. \n", " - This class must have a `step()` method which describes the primary individual behavior. \n", "2. There must be a *model* class, which should inherit from `mesa.Model`. \n", " - The `__init__()` method of this class is responsible for creating agents with their properties, as well as the space (often a grid) on which the simulation unfolds. \n", " - This class must also have a `step()` method which provides a complete description of what happens in a single model time step. Often, this involves using a `Schedule` to call the `step()` method of each of the agents in some specified sequence. \n", " \n", "Let's write a very simple model that demonstrates some of these requirements. Our model won't really do very much yet, but it will demonstrate the key techniques of defining the agent and model, adding agents to the model, and calling the `step()` methods. " ] }, { "cell_type": "code", "execution_count": 1, "id": "41b6285e", "metadata": {}, "outputs": [], "source": [ "from mesa import Model, Agent\n", "from mesa.time import RandomActivation\n", "\n", "class ToyAgent(Agent):\n", " \n", " def __init__(self, name, model):\n", " super().__init__(name, model)\n", " self.name = name\n", " \n", " def step(self):\n", " print(f\"Hi, I'm Agent 00{self.name}!\")\n", "\n", "class ToyModel(Model):\n", " \n", " def __init__(self, n_agents):\n", " \n", " self.schedule = RandomActivation(self)\n", " \n", " for i in range(n_agents):\n", " agent = ToyAgent(i, self)\n", " \n", " # important and easy to forget! \n", " # this line \"registers\" the agent\n", " # with the scheduler so that the \n", " # agent's step() method will be\n", " # called when the scheduler's \n", " # is. \n", " self.schedule.add(agent)\n", " \n", " def step(self):\n", " self.schedule.step()" ] }, { "cell_type": "markdown", "id": "6112949c", "metadata": {}, "source": [ "Let's demonstrate the behavior of our toy model: " ] }, { "cell_type": "code", "execution_count": 2, "id": "323b679e", "metadata": {}, "outputs": [], "source": [ "TM = ToyModel(10)" ] }, { "cell_type": "code", "execution_count": 3, "id": "f64f2900", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hi, I'm Agent 007!\n", "Hi, I'm Agent 006!\n", "Hi, I'm Agent 003!\n", "Hi, I'm Agent 008!\n", "Hi, I'm Agent 004!\n", "Hi, I'm Agent 005!\n", "Hi, I'm Agent 002!\n", "Hi, I'm Agent 009!\n", "Hi, I'm Agent 001!\n", "Hi, I'm Agent 000!\n" ] } ], "source": [ "TM.step()" ] }, { "cell_type": "markdown", "id": "85ccfd11", "metadata": {}, "source": [ "Observe that, each time we call `TM.step()`, the model sweeps through the various agents and calls their individual `step()` methods. This is because we created a `RandomActivation` schedule, and added each of the agents to this schedule.\n", "\n", "With our architecture in place, our next step is learn how to implement more interesting behaviors. \n", "\n", "## Spatial Grids\n", "\n", "The Schelling model usually evolves on a grid. At the moment, we don't have a grid incorporated. Fortunately, this is easy to bring in. We simply need to add a `SingleGrid` object with specified width and height. The `torus` argument of the grid determines whether the edges \"wrap around.\" If it is selected, then walking off the left side of the grid will put you back on the right side. This is often visualized as allowing the grid to lie on the surface of a torus, or donut: \n", "\n", "
\n", " \"\"\n", "
A toroidal grid.
\n", "
\n", "\n", "The modifications we need to make to our previous code are relatively simple: \n", "\n", "1. We need to give each `ToyAgent` a `pos`ition. \n", "2. We need to give the model a `grid` instance variable. \n", "3. We need to modify our initialization of agents so that we call `self.grid.position_agent(agent, pos)` in order to place each agent on the grid. " ] }, { "cell_type": "code", "execution_count": 4, "id": "6e4eacad", "metadata": {}, "outputs": [], "source": [ "from mesa.space import SingleGrid\n", "\n", "class ToyAgent(Agent):\n", " \n", " # adding a pos instance variable so that each agent can remember\n", " # where they are. Note that the pos can take the place of the name. \n", " def __init__(self, pos, model):\n", " super().__init__(pos, model)\n", " self.pos = pos\n", " \n", " def step(self):\n", " print(f\"Hi, I'm an agent at {self.pos}!\")\n", "\n", "class ToyModel(Model):\n", " \n", " # need to specify width, height, and density of agents\n", " # in the grid. \n", " def __init__(self, width, height, density):\n", " \n", " self.schedule = RandomActivation(self)\n", " \n", " # create the grid\n", " self.grid = SingleGrid(width, height, torus=True)\n", " \n", " # loop through the grid, and add agents so that the \n", " # overall density is roughly equal to the passed \n", " # density\n", " for cell in self.grid.coord_iter():\n", " x = cell[1]\n", " y = cell[2]\n", " if self.random.random() < density:\n", " \n", " agent = ToyAgent(pos = (x, y), model = self)\n", " self.schedule.add(agent) \n", " self.grid.position_agent(agent, (x, y))\n", " \n", " # this doesn't change. \n", " def step(self):\n", " self.schedule.step()" ] }, { "cell_type": "markdown", "id": "b8f3a9fd", "metadata": {}, "source": [ "Now we can again instantiate our model. This time, we need to pass `width`, `height`, and `density`. Here, we're creating a 10x10 grid in which roughly 10% of cells have agents in them. " ] }, { "cell_type": "code", "execution_count": 5, "id": "42cea0f8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hi, I'm an agent at (5, 0)!\n", "Hi, I'm an agent at (1, 3)!\n", "Hi, I'm an agent at (8, 5)!\n", "Hi, I'm an agent at (9, 4)!\n", "Hi, I'm an agent at (1, 4)!\n", "Hi, I'm an agent at (4, 5)!\n", "Hi, I'm an agent at (9, 1)!\n" ] } ], "source": [ "TM = ToyModel(10, 10, 0.1)\n", "TM.step()" ] }, { "cell_type": "markdown", "id": "cfce30e5", "metadata": {}, "source": [ "It's also possible to directly extract the grid and visualize it using familiar tools. In a later lecture, however, we'll see some much better ways to visualize the grid. " ] }, { "cell_type": "code", "execution_count": 6, "id": "04bb3a84", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "import numpy as np\n", "from matplotlib import pyplot as plt\n", "plt.imshow(np.array(TM.grid.grid) == None)" ] }, { "cell_type": "markdown", "id": "f5ccb63e", "metadata": {}, "source": [ "# A Basic Schelling Model\n", "\n", "We're now ready to construct a simple version of the Schelling model. Here are the remaining ingredients we need to bring in: \n", "\n", "1. Agents need to have *types* associated with them. \n", "2. The agents `step()` method should check whether the agent is \"happy\" (i.e. not surrounded by too many neighbors of different `type`, and move them to an empty grid cell if not. The `SingleGrid` class we've used to create the grid provides several useful methods for handling this logic. " ] }, { "cell_type": "code", "execution_count": 7, "id": "dc90016f", "metadata": {}, "outputs": [], "source": [ "class SchellingAgent(Agent):\n", " \n", " # adding a pos instance variable so that each agent can remember\n", " # where they are. Note that the pos can take the place of the name. \n", " def __init__(self, pos, agent_type, homophily, model):\n", " super().__init__(pos, model)\n", " self.pos = pos\n", " self.type = agent_type\n", " self.homophily = homophily\n", " \n", " def step(self):\n", " \n", " pct_similar_neighbors = np.mean([\n", " self.type == other.type for other in self.model.grid.neighbor_iter(self.pos)\n", " ])\n", " \n", " if pct_similar_neighbors < self.homophily:\n", " self.model.grid.move_to_empty(self)\n", " self.model.moved += 1 \n", " \n", "class SchellingModel(Model):\n", " \n", " # need to specify width, height, and density of agents\n", " # in the grid. \n", " def __init__(self, width, height, density, homophily):\n", " \n", " self.schedule = RandomActivation(self)\n", " \n", " # create the grid\n", " self.grid = SingleGrid(width, height, torus=True)\n", " \n", " # loop through the grid, and add agents so that the \n", " # overall density is roughly equal to the passed \n", " # density\n", " for cell in self.grid.coord_iter():\n", " x = cell[1]\n", " y = cell[2]\n", " if self.random.random() < density:\n", " \n", " agent_type = np.random.choice([\"triangle\", \"square\"])\n", " \n", " agent = SchellingAgent(pos = (x, y), \n", " agent_type = agent_type, \n", " homophily = homophily, \n", " model = self)\n", " \n", " self.schedule.add(agent) \n", " self.grid.position_agent(agent, (x, y))\n", " \n", " # this doesn't change, except that we will add a counter for the number of happy agents\n", " # who don't move in this timestep\n", " def step(self):\n", " self.moved = 0\n", " self.schedule.step()\n", " print(f\"{self.moved} agents moved in this timestep\")" ] }, { "cell_type": "code", "execution_count": 8, "id": "f71e9d70", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "135 agents moved in this timestep\n" ] } ], "source": [ "SM = SchellingModel(20, 20, 0.9, 0.5)\n", "SM.step()" ] }, { "cell_type": "markdown", "id": "5e8c3cb4", "metadata": {}, "source": [ "Here's a function to plot the model state. Dark purple squares are empty; green squares are agents of type `triangle`, and yellow squares are agents of type `square`. " ] }, { "cell_type": "code", "execution_count": 9, "id": "2dab2cc8", "metadata": {}, "outputs": [], "source": [ "def viz_state(SM, ax):\n", " G = np.array(SM.grid.grid)\n", " to_viz = np.zeros(G.shape)\n", " \n", " for i in range(G.shape[0]):\n", " for j in range(G.shape[1]):\n", " if G[i,j] is not None:\n", " if G[i,j].type == \"triangle\":\n", " to_viz[i,j] = 1.0\n", " elif G[i,j].type == \"square\":\n", " to_viz[i,j] = -1.0\n", " ax.imshow(to_viz, cmap = \"Spectral\", vmin = -1.5, vmax = 1.5)" ] }, { "cell_type": "markdown", "id": "538352b5", "metadata": {}, "source": [ "Now we're ready to visualize the evolution of our model. " ] }, { "cell_type": "code", "execution_count": 10, "id": "c2056f97", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "246 agents moved in this timestep\n", "193 agents moved in this timestep\n", "150 agents moved in this timestep\n", "119 agents moved in this timestep\n", "89 agents moved in this timestep\n", "78 agents moved in this timestep\n", "68 agents moved in this timestep\n", "68 agents moved in this timestep\n", "67 agents moved in this timestep\n", "51 agents moved in this timestep\n" ] }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjwAAAD1CAYAAABUdy/PAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAAAha0lEQVR4nO3df/Bld13f8dcbNkBJmhhTVholiWIZJFVoKqTY6rDT6AS1E3dNLBqXFusEZKgyJagVaeMMyoSxY9WZGJxpg1mVwU13q6BkUAyphWHMTO008lMSFhICSfiV7DKRhuTTP87Z6c2573v3vZ/P59xz7uc+HzPfmf3me+/59T7n3E/Oed33sZSSAAAAWvakqRcAAABgbAx4AABA8xjwAACA5jHgAQAAzWPAAwAAmseABwAANG/jAx4zu8DMTpjZkzc9b9RFLdtCPdtBLdtCPesYfcBjZsfM7LKTv6eUPp1SOiul9NjY815YhpeY2b0jTt/M7Hoz+0L/8xYzs7HmN5UdqeU+M7vNzB4ys2NjzWcOdqSerzezvzaz42b2STN7/VjzmtKO1PK1Zna3mT1sZveZ2a+Z2Z6x5jelXajnwnyeYmYf3cS8uKVVxzWSfkjS8yV9h6QflPTKKRcI2b4i6b9KavKDcQeZpJdLOlfS5ZJeY2Yvm3aRkOmdki5JKZ0t6R+qO9/+9LSLhApeL+mBjcwppTTaj6RDkh6X9IikE5J+VtJFkpKkPf1r3ifpTZI+0L/mnZLOk/R7kh6WdIekixam+VxJfyrpi5I+JulHFv72/ZI+LOm4pM9IulbSmf38H++nf0LS+eoGez8v6S5JX5D0B5K+vp/OyWW8RtJ9kj4r6XVr1vMDkq5Z+P3fSPrgmNt20z+7UsuF+V8m6djU25161qnnwnL8hqTfnHr7U8uyWvbL/meSbph6+1PP/HpK+mZJH5H0Ukn3jr5tN1C8Y5IuW/jdK9wnJD1b0jn9hv+4ug+cPZJulnRT/9ozJd0j6RX93y6R9HlJF/d//6yk7+7/fa66/xuQpJcMN6ak10r6oKRvkvRUSW+V9PbBMr69n+e3S3pwcT0G03pI0qULv3+npONTHzjU8vRruTDNpgc8u1bP/r0m6a8kvWrqbU8t82op6cfUfaCn/rXPn3rbU8+ier5L0n5vXmP8zOWW1k0ppbtSSg9Jereku1JKf5ZS+pqkw5L+Uf+6H1T3IXRTSulrKaX/Jem/Sbqy//ujkp5nZmenlL7U/32VV0p6Q0rp3pTSVyVdJ+nKwT3hX0opfSWldKekmyT96IppnaVu0HPSQ5LOajHHE7DttcQTtVTP69T9H+pNkRVv0NbXMqX0+6m7pfUcSTdKuv80t0FLtrqeZrZf3QDuaNbaZ5jLgGdxp33E+f2s/t8XSrrUzL588kfS1ZKe2f/9h9VdnvuUmd1uZi9eM88LJR1dmM5HJD0m6RsWXnPPwr8/pe6SnueEpLMXfj9b0onUD2F3zLbXEk/URD3N7DXqsjw/0J+od1ETtZSklNLfSPqQpBtO9dqGbW09zexMSW+R9G/XrmFlm0i41/zQv0fS7Sml73VnlNIdkq4wszMkvUbd/cVnrViGeyT9RErp/cM/mNlF/T+fJemj/b8vUHdf0vMhdQG6v+x/f37/31qzC7XcJTtRTzP7CXW5g+9JKY3+TZCJ7EQtB/aou6XTotbr+Q/U3QL7i/5GyFMknWNmn5P0T1JKxyIrdro2cYXnfknfUmla75L0HDM7aGZn9D8vNLNv67/adrWZnZNSelTdfd6TX+G7X9J5ZnbOwrRulPTLZnahJJnZM8zsisH83mhmTzezi9Xd/3zHiuW6WdK/M7NvNLPzJb1O0tuqrPG8NF9LM3uSmT1N0hndr/Y0M3tKpXWem12o59WSfkXS96aU7q60rnO0C7X8STPb2//7eZL+vaT3VlrnuWm9nn+tbmD0gv7nJ/v5vUBPvEJU1SYGPG+W9Iv9JbBrSyaUUjou6fskvUzdqPFzkq5XF56SpIOSjpnZw5JeJenH+/d9VF2Q6u5+Oc6X9OuS/kjSe8zsuLog1qWDWd6uLhj2Xkm/mlJ6z4pFe6u6lPyd6gr5x/1/a80u1PJ71F0O/hN1/3fyiKRVr912u1DPN6n79sod1jVuO2FmN5as60ztQi3/qaQ7zewr6o7PP5H0CyXrOmNN17PPEn3u5I+6b4893v8+Wq8h282YyXr9pblPSjqjD4BhS1HLtlDPdlDLtmxDPecSWgYAABgNAx4AANA8bmkBAIDmcYUHAAA0jwEPAABo3trGgw8cvHzpfter91+z9Lobjv72KWfkvc9zeP+5S//tqqNfCr2u1vtWvTeynpL0jJt/Lms5Hnz59Uv/be+hW6s9nsKrp2e4/NLyskXr6W2z3H2oRHR5I6K189SqZ7SWkW1du5bR90Z4+2LJ8TScXrRunrkcm7mi6+7VeLi9o9OKnFtWzdNT87wxh2NzuF29fd2T+9kUnUfJ53J0HXL3Kc+qWnKFBwAANI8BDwAAaB4DHgAA0Ly1GR7vft9h74X7A/eVC+735d4XjOZ1PEV5heG9R+d9kXzBJrgZj4J76rnvi9xbjt6njmaEcnMCV8mZfmhK4/K2Yc1aeqLbdfjfopmbcJbOOQe5GYnld85WdPkjx4m3b0TzNN70hseAt/9Hl98970U/B4bLMXIWMFf02BzuxyV5QY/33sOBz53oceidG6Of3yXrFcUVHgAA0DwGPAAAoHkMeAAAQPMY8AAAgOatfZZWSrct/TG3SVQ0uFQSTI00LooGg6PLlrvu0WUz21etudmVR44s1TO3EWNJ4Dw3nBZtmlXS7DA3OD6HxoO5TfpKwoK52zXajMxTs3loSRPMuTQezA33R+U2q4sqWd7IfrXpepY0HhzaRAi75rHpKTleh0pryRUeAADQPAY8AACgeQx4AABA8xjwAACA5q0NLc/5qa+R5SgJC5c8STj36fHe+8YORuYuf+3wt2fsjsAlIejItLx9aNPByEidSp50nTu9ktqO/cWGqClCy2OL7sdjB11zvyBS8vmx6WNzbLmfayXH5thfUin9cghXeAAAQPMY8AAAgOYx4AEAAM077aelR5+m++DRJ/7uPZHVu+9b0ggsl5vbcF4XfuJtpfdtQm62xVt+dz0LahfJgUWbWg33R2nF/WBn/xuul/e+TTzpN0eomVdwWjVzbUWNK51aeiLnpZKcwBRqZrKi781txhkVPUdEjnXvad1zfYL62HL3g9z81CrRY2w4jzE+I7nCAwAAmseABwAANI8BDwAAaB4DHgAA0Ly1oeXcJ6N73ABScFrRIHPNpmIeNwAaWYfg++YSrouGeSOi+0skxOptHy+k6E4/GrwMhHxrNiwcW24TzGgY3J1n4Lxx2HlfNMgc/UKBZ2l6LRybA7VDxqHzaMk+tD9/eSPniCm+CLNp0fNsdlPZ4BcKoufBUFNQ58sJpbXkCg8AAGgeAx4AANA8BjwAAKB5DHgAAEDz1oaWowGh7CBzsMtv7hPOaz5tW4qHoyKhwdpPF64pd7uVBHdzO6SWTD/cpXmwX9XuKL1pS8vv1K2kq3Wks7gXSPTe5wYjvaB65vEUPgcFuzvPQcm5xdvekc+BSFfrVaJfAsid1ly62kdEurpHQ8W55yT3aQre67z6OvtK9PNkWHPviyalteQKDwAAaB4DHgAA0DwGPAAAoHkMeAAAQPPWhpY9uZ1zo6HUaHfkyOtqh6JLgpyRZfO27S1ZU69vuLzR4Fy462tmSDEarA+HKiOhu2D33zmoGQiNvi/3mIh2do92eI1w958Zh1xDx1NBaHnsjuHu58eMv8AxptC2KNheY3eYjn4G5Kr9pASJKzwAAGAHMOABAADNY8ADAACax4AHAAA0b21oOTd8KDmBo2AHxmioKhJmdIOMXvA4GGT21iEatByaS8g1GkIPh48HomHSsYOR0ZB4JBBbs+N2TdH5DZe/dkdyN/gaeN/YIUjPnDtkRwPVtes3ppLtvU3rOeR2EXdeN3YH/ppB5uhnZK4xQtdc4QEAAM1jwAMAAJrHgAcAADSPAQ8AAGiepZRW/vGBg5cv/TEaHAt15nVEO31GliPakTXakblmADcayNp76FYLzTQgpduW6pnbzXKKAGFJoD13H/J4+1W0W7fZvir19I7NsYPlJcbu8Jr75YGSrs23HDhQ7dgsqedw3TdxbI79JYPcupR8oWDMYzO6DEO1j83ouaum3M/S6LK6TyhYcWxyhQcAADSPAQ8AAGgeAx4AANC8tRkeL/Oxifv9EWM3DKvZ9KgkD1QzJ3DlkSNL9ayZbRlb7fvPNZ8kHs2B1coJRPNYNdexxLB20XvxtZs81qxvzXxdSe5jinqOncmqmeX0eOeSTWd4pjjPjp3hqdls1dsHos2QV9WSKzwAAKB5DHgAAEDzGPAAAIDmMeABAADNW/u09GhwzDMML5UE63IbA9ZuKJjdIKtgO+rAgdjrAqLzjATbSgJ3JQ0EI68pqfu2hLijQcOxn2icva2Dx5L2x5oM5oag51jbdWoGlEuOk0g4NVwT53WHvek7+8LwiePR7eO97pZ6p9qQkqaXkWn5x/6p6xb9nIsqafabO629h/a57+cKDwAAaB4DHgAA0DwGPAAAoHkMeAAAQPPWdlou6cwb6axa8tTjmk8k90SXLaLk6d1TdFr2jP1k39xQXLQbZ81uoiVB11r1jHZa9uQuf0l33bHnWXKM5S5HzU7LJfUcqv2FgprdnUvCr9HPhtx5broLuif3yz7RuuVuQ09JLT25T1D3A+g8LR0AAOwoBjwAAKB5DHgAAEDzGPAAAIDmrQ0te4+5nyLw6IksRzS0WBJuzA3IRoOANUPLXpjOkxse8+SG9aLLUSIagMvdd8cMoXu1zO1oOnbYubaSc0nNda95bHrn2qjc4G70+KrZEThqii7EtULoJbUcqn1ebLGWnlUBdK7wAACA5jHgAQAAzWPAAwAAmseABwAANG/Puj+63XUzg25XKb/rcfRx9cMgWjRkmTt9acV6BebphiVHDoXW7LJZe/o3OO8dbse5hGajanciPZVot9WhaJA/t8u6VDcYGd6nnOUdLod3rE6xn5V0hc8VrdMU56qa+4s7LS+EXml+NTv8R7d9tJZjB5Q9o9fSccsB/79zhQcAADSPAQ8AAGgeAx4AANA8BjwAAKB5a0PLJYGpmrzA14NHl183DIuVdFAeO+BbEgjPVRL2HL432qE2WoNIuPZwaI7x2nmvO+wsh7evRabv1XNVmO50ecvubcPItnAD+oF1XiW3U3e0btF9KhKo9r50MIWSrvBL6xQNHju8/T93G0WDtNHPjykCtzlKjqdhnbxjOnoenIuSz+HauMIDAACax4AHAAA0jwEPAABoHgMeAADQvLWh5XDHyP2nDpxGRcNMXjBs2K23dvfScDg7sD28EKHboflApZSr4kHRyHu9IGN0++SGj8NBt2gnUq9LdiA4O3YH3IhoQNkzXP7cbuHetFbJDb1HA6A1g69jd4r2ePX09sVQuD+4zbxp5YaKvXq6Qd2CL43kdnfO7UA+ttD5LFiP6Bcwcvd/T/SYiO7bmwgyc4UHAAA0jwEPAABoHgMeAADQPEsprfxjSrct/TF63254HzB6zz4qcg82ej/ae13NJ0OXNF665cABC70w4IGDl68u9mkqyZB4IttjE0/rzs3nbLqeJbWMHDvRWkZzcqGnQAenXyLSpC/yPkky2zeLYzOyjWo3eoy8bxNZqOGyRXNJ3jrtPXTr5MdmzVp6IttnqkaQkTFDNHu16tjkCg8AAGgeAx4AANA8BjwAAKB5DHgAAEDz1jYeLAn0RUSDbl54KRJE85qnRZthRcN73vSG8y1qgFix8aAnt9GjGwgvCAxG5ulua2da0ac7lzRoW5qW89/cIOHI9RzKDvcXNKXztuFSMDLa+K0gGBkJzeaeu6YSPWcORQPKnlBQNPqE9soh9KWmoBuYZy2RJ6H723D5fTW/RBJ+X+XtGnlSfOR9krT30D73tVzhAQAAzWPAAwAAmseABwAANI8BDwAAaN7aTsvRjpGR7o0l3YZzn8jsiYaiSzp2RpYruj2m6LQcXfeIklBozRBbUXB8IBoQHLOeY3dz9WziGB4qeaJ35HUlHZ/n2gXdU7LPjl07T+45v6Sj9ByPzegxF30ifO4xMXbH81XzzbWqazZXeAAAQPMY8AAAgOYx4AEAAM1jwAMAAJq3NrQMAADQAq7wAACA5jHgAQAAzWPAAwAAmseABwAANI8BDwAAaB4DHgAA0LyND3jM7AIzO2FmT970vFEXtWwL9WwHtWwL9axj9AGPmR0zs8tO/p5S+nRK6ayU0mNjz3thGV5iZveOOP3rzOzRfoc8+fMtY81vKrtQy34el5jZ/+jreL+Z/cyY85vKLtTTzN49OC7/r5ndOdb8prIjtXyqmd3YH5NfNLN3mtk3jjW/Ke1IPb/OzH7HzB7of64ba14ncUurnnf0O+TJn7unXiCcPjP7e5JulfRWSedJ+lZJ75l0oZAtpfTSxeNS0gckHZ56uZDlZyS9WNJ3SDpf0pcl/eaUC4Qivybp6ZIukvQiSQfN7BWjzjGlNNqPpEOSHpf0iKQTkn62X7kkaU//mvdJepO6E9EJSe9U90Hze5IelnSHpIsWpvlcSX8q6YuSPibpRxb+9v2SPizpuKTPSLpW0pn9/B/vp39C3cHyJEk/L+kuSV+Q9AeSvr6fzsllvEbSfZI+K+l1a9bzOkm/O+a2nPpnh2r5K5IOTb29qWedeg7W+SJJj0n65qm3P7XMOjZ/S9JbFn7/AUkfm3r7U8/sen5e0gsXfv8FSX8x6rbdQPGOSbps4XevcJ+Q9GxJ5/Qb/uOSLpO0R9LNkm7qX3umpHskvaL/2yX9Rru4//tnJX13/+9zJV3S//slku4dLNdrJX1Q0jdJeqq6/6N/+2AZ397P89slPbi4HoNpXSfpoX5n+pCkn5r6oKGW2bX8c0m/ru5E8oC6E8kFU2976plXz8F0/4Ok90293all9rH5nZLer+6D9+mSfl/Sf55621PP7Hp+XtKLFn5/g6QvjbpdZ1K4Nyz8/T9JevfC7/9C0v/u//0vNRgB9hv8P/b//rSkV0o6e/Aar3AfkfTPF37/+5Ie7XeIk8v43IW/v0XSf1mxjs/rD8InS/qufgf60akPGmqZVcuPq7tU/kJJT5P0G5LeP/W2p5559RxM9xOS/vXU251aZh+bZ6v7ME2Svibpr9RfXWjtZ0fq+buSjkj6u+qiA3dJ+uqY23UuGZ77F/79iPP7Wf2/L5R0qZl9+eSPpKslPbP/+w+ruzz3KTO73cxevGaeF0o6ujCdj6i73P0NC6+5Z+Hfn1I3qFmSUvpwSum+lNJjKaUPqLtCcOWaebdsq2vZL+PRlNIdKaW/lfRLkr7LzM5ZM/+WbXs9JUlm9s/6Zbll3esat+21/C11/xNynrorCEckvXvNvFu37fX86X45/0bSH6obzI76hZRNDHhSxWndI+n2lNLXLfyclVL6KUnqP6SukLRX0n9Xd39x1TLcI+mlg2k9LaX0mYXXPGvh3xeouy8ZkSTZaazXttiFWv6fwTxO/pt6rjfXep70ryQdSSmdyFi3bbALtXy+pLellL6YUvqqusDyi/ovGrSm+Xr2dbw6pfTMlNLF6sYjf1m0pqewiQHP/ZJqfUX7XZKeY2YHzeyM/ueFZvZtZvYUM7vazM5JKT2qLrh18it890s6b/B/6TdK+mUzu1CSzOwZZnbFYH5vNLOnm9nF6u5/vsNbKDO7wszOtc6L1I1c/7DSOs9J87WUdJOk/Wb2AjM7Q9IbJf3PlNKXq6z1vOxCPWVmf0fSVZLeVmVN52kXanmHpJeb2Tn9sflqSfellD5fZ7Vnpfl6mtmzzew8M3uymb1UXdj5TZXW2bWJAc+bJf1ifwns2pIJpZSOS/o+SS9TN2r8nKTr1YWnJOmgpGNm9rCkV0n68f59H1V3uezufjnOV3fb6Y8kvcfMjqsLYl06mOXt6u77v1fSr6aUVn09+WX9646rC4tdn1L6nZJ1nanma5lS+nN13xb4Y3Wh5W+V9GMl6zpjzdez90PqvlRwW8k6ztwu1PJaSX+r7hbIg+puw+wvWdcZ24V6/mNJd6r73HyzpKtTSh8qWddTsT48hAVmdpGkT0o6I6X0tYkXBwWoZVuoZzuoZVu2oZ5zCS0DAACMhgEPAABoHre0AABA87jCAwAAmseABwAANG/Puj8+cPDy0P2uV++/Zum/Hd5/7hN+f/Dl15/Ocp3SM27+uWrTuurol5b+2w1Hf3vpv3nr6fHem2vvoVurNbyL1tPbtrXrF5nnsC7edo28T1reH1e9zlNzX65VT6+WudtiE8fmcB7Rfcw75kqOzTnWUiqr55C3r5esZ249PSXHpve6Uy3X6Rjz2Izux8PtGP1sGlv0+IqaqpZc4QEAAM1jwAMAAJrHgAcAADRvbYbH492rPVxlUU5P7v29SN5Ikq5S8HXBHMhcudsj8Lra95G9et4w+D0307DqdZH7yKuWbVtE9tnhdt4Etx5eDsR5XfgYdt67TbXMzZhtYjncWg2429qp3SaO4alFP2OG+7ubxzqavxy52dcbgvm6Eps4L3GFBwAANI8BDwAAaB4DHgAA0DwGPAAAoHmnHVr2gmg1mwCWyA1RRUNz3rq74b39mYG+CXjh40hguyQ4FzWsZ+1wfLQGkf17DvWMNu4bu5aRc4QXgnRDnI5oUDUcFB2YorGbJ7fRZu3ljwTCvddEv+ASDSP7Ieh5fPacSvizI/C+sdVuAOruj07dNvEZwxUeAADQPAY8AACgeQx4AABA8xjwAACA5llKqx+gHX269tjCQajMaXlqPsG35Im3UzwtfWy5T2Mv6bRcErAbzneuT2SeQu6xWRKCLOmuWzMEyrH5/9WuZ/TLMXOs57bXMjqt2k8ZqBm252npAABgZzHgAQAAzWPAAwAAmnfajQenkHtvr6TxVTR34zVHGs639hNvt13N++7RrJXbPDD4ujk0FZyr3GMzmu+I2uYnac9J7r4ebZIYbR7q7gveOTk4vV00xXmrJPu6ic9ErvAAAIDmMeABAADNY8ADAACax4AHAAA0b3ah5WiDo0hYMtq8KhxuDD6ZN7eJ3q6EY3PXPbp9vCCj95Ts6FN8I3a5nrVEA9BeLaPHcKSJJLWsI7pto6HW3EZ31LNc9Gnv0Rq5n+mB95U2IeYKDwAAaB4DHgAA0DwGPAAAoHkMeAAAQPNGe1r6MChWEkyLPk23ppKur8PlncPTtaWyeg63R0kX25InnOcqqedQyXLN4YnMucdmVO6XDDy1n9Lc4rE5FA12RsO8pUHRU83TE63xHOtZs5Ylgeuxw9q1azk0xnmWKzwAAKB5DHgAAEDzGPAAAIDmMeABAADNq9Jp2Q2EBoJKY3dljHK7LweXwzN8Xc1l3QQ3jDZYp5Lg6Ngiy197+q11bq1d30iH400YrpfXLXbbajnctu65y+lO7R4TzrncDbDvz6tneNsGv2TQWj1zO4G7x6v3ZYRA0LikltEg81S4wgMAAJrHgAcAADSPAQ8AAGgeAx4AANC80w4t1w745qrZ/TPaXTRXNGRWs7NwVDRkNgwu1q6v2037aN60vHp6Yb3cdRg7RJ9r7oHBHGPXcs68c1Ck63zNc5dUvxP30NhfMthmtTuNj11Lj/e5FtlHa37Gn8QVHgAA0DwGPAAAoHkMeAAAQPMY8AAAgOaddmg52l1x7JDr2KLhqEj4Khq8zA3plsjuQrqJYOTI3Xlzw3TR9206hB4N348dUvSM3e3WW6fcc85cOmm7+8/+5WVbWt4tO9d6co9Nz1zqORRZrtoB9LFFzuOSQvuoe0w7HcNP5zzLFR4AANA8BjwAAKB5DHgAAEDzqjwt3TNFZqfmE5lrZjKiT5OPPMl2E+Zy33js++w1m2h6+8YcGgHWzLZMwT0OR35K87Y9BXrsenr79jBLEW28GW2kl5v78GxTPcc+90bye9HMU/T8mSvcJNHJta3CFR4AANA8BjwAAKB5DHgAAEDzGPAAAIDmjRZazhVtOFUzfBUVDWnlNo/ylm3voX2h985VtMmdF1CLBAujYbqSYOdSE02n+VXkKdbS9tdzKBrWHjbVLDk2S+obaYjqBXDnUsuxA8rRUOgwOB49x3mBc6+JaXQ9l/a/SGNGzaeeY8o993rvi27DktD1cL7e9KPNeVfVkis8AACgeQx4AABA8xjwAACA5jHgAQAAzasSWo48DXwT3V1DYeFgB+VoCNUTCW5FnwR7S2iOdUXCbtF65oZJPdGAcvTp9LXXYWgO3VzHPjbdLxQE3lfSubVkeSPvjYait/3YHFv0HFdiuP9FvkQizePY9ERC9VHhYyx3npW7Qi/NN/PLSutwhQcAADSPAQ8AAGgeAx4AANA8BjwAAKB5llJa+ceUblv6Y83gbgk3EDeTsF5EdPlvOXDAas3zgYOXry72KQzDbrW39bbXM9px2GxflXqW1HLsYzOyLeZc22gwslYtpXkfm55IV9yx9zNPSah1DsfmFOfZYe1qd6qvqfRzkys8AACgeQx4AABA8xjwAACA5jHgAQAAzRsttJzbyXIu4ai5qBlaLqnnkNthd8uDx5tQq55XHjmyVEsvLOzJ7gS+I7WMdlnfe+jWnQ0tb5PocVGrnnP+QsG2K/1yCFd4AABA8xjwAACA5jHgAQAAzWPAAwAAmrdnUzPKfVS9RAivFm873hB877BWh4Pv80JmBPPKucHao8uvc4+xwe/efuEdr96+0mItve1xOPNLGFHedowGNCPnx2gQu8V65n6BZpL5ZdbS0+LnpvtlmdN4P1d4AABA8xjwAACA5jHgAQAAzWPAAwAAmrc2tBwNuYaCdF4oLxhknouaIcIplARdh+sUDZdHQ9FTGDsgHw0XblpkHaOdtCMhy02oWUvvOPeOk72H9mVNP6rmuWXbjs1cblfl/cvb0e+cXaee0S/o5HJr6a73dgfQxzh/coUHAAA0jwEPAABoHgMeAADQvLVPS/ee+urdi6x5zzJ6fzJyf3sTmZvc+4zRp5RP8UTm3G0U3d6emtMfu55uY7oNP2F77CcyhzNamY0lN9GQMlKT6P45xdPSax47nm1rYLcLx+aw5iXr45lLY8nczF1pLbnCAwAAmseABwAANI8BDwAAaB4DHgAA0LzTDi17Ik/6jYZLS4KpuaG22iLLMZfQ8tgB5ZJAeyRgOkU9o7XzbDoYWbPh2RQN1cbmNmwLmuILBZ7h+bdknbbtXDs0h3rWDC2XyA08z6WZbkk4m9AyAADYWQx4AABA8xjwAACA5jHgAQAAzVv7tPQoN8A2eEKtGzZyws7R4OvhQLhrqoBypOPztllaT+cJxFHR8GvNAN8uGP2JzM7rcju8TvUk+RaOxRzR82r0XBs6x22gq+/SFwqcp9pvk+F2Lel4Hqmb5B/XUxiuwxi15AoPAABoHgMeAADQPAY8AACgeQx4AABA89Z2WgYAAGgBV3gAAEDzGPAAAIDmMeABAADNY8ADAACax4AHAAA0jwEPAABo3v8D3C0Eg9Hp5DgAAAAASUVORK5CYII=\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "fig, axarr = plt.subplots(2, 5, figsize = (10, 4))\n", "SM = SchellingModel(20, 20, 0.8, 0.7)\n", "\n", "t = 0\n", "for ax in axarr.flatten():\n", " ax.axis(\"off\")\n", " ax.set(title = f\"timestep {t}\")\n", " viz_state(SM, ax)\n", " t += 1\n", " SM.step()" ] }, { "cell_type": "markdown", "id": "f50010af", "metadata": {}, "source": [ "We observe the characteristic separation of an initially spatially mixed population into large regions of homogeneous types. \n", "\n", "In coming lectures, we'll learn how to visualize these processes more gracefully; how to collect data from simulations; and how to implement more complex models. " ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.11 64-bit ('PIC16B': conda)", "language": "python", "name": "python3811jvsc74a57bd000ce6b323a8a0b9f3b69029fa338424bb9ad4dbfdf789891ecc2bf5c65882714" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.11" } }, "nbformat": 4, "nbformat_minor": 5 }