Chapter 12 The Emergence of Inequality: The sugarscape Model
In our exploration of agent-based models, we began with the simplest possible system—a random walker moving aimlessly across a grid—then progressed to the Schelling model, where individual preferences generated striking patterns of residential segregation. Now we turn to perhaps the most consequential question in social science: how does economic inequality emerge and persist in societies? Like Schelling’s surprising demonstration that mild preferences could produce extreme segregation, our wealth distribution model reveals how random economic exchanges, even when entirely fair at the individual level, can generate substantial inequality across a population.
The model we examine here strips away most complexities of real economic systems to reveal a fundamental mechanism. Agents begin with equal wealth and engage in random trades with neighbors they encounter while moving across a spatial grid. No agent possesses superior skill, information, or opportunity. The trading rules impose no systematic bias favoring any particular agent. Yet despite this egalitarian setup, wealth inequality emerges inexorably from the stochastic dynamics of exchange. This stark result challenges intuitions about economic fairness and raises profound questions about the origins of inequality.
12.1 The Mathematical Framework of Random Exchange
Our economic agents inhabit a toroidal grid where they move randomly and trade with agents they encounter in the same location. Each agent i possesses wealth W_i(t) at time t, with all agents initialized to equal wealth W_0. The trading mechanism operates through pairwise exchanges where agent i and agent j in the same cell execute a transfer:
ΔW = U(0, min(W_i, W_j))
where U(a,b) denotes a uniform random draw from the interval [a,b]. The wealth updates follow:
W_i(t+1) = W_i(t) - ΔW W_j(t+1) = W_j(t) + ΔW
This exchange preserves total wealth—the sum ΣW_i remains constant—but redistributes it randomly between trading partners. The implementation captures this elegantly:
def trade(self, other_agent):
"""Trade with another agent - transfers wealth randomly"""
if self.wealth > 0 and other_agent.wealth > 0:
trade_amount = min(self.wealth, other_agent.wealth)
transfer = self.random.randint(0, trade_amount)
self.wealth -= transfer
other_agent.wealth += transferThe constraint that transfers cannot exceed the minimum wealth of the two traders ensures that no agent accumulates negative wealth, maintaining economic realism. This bounded exchange distinguishes the model from unbounded random walks in wealth space, creating an absorbing boundary at zero wealth that profoundly influences long-term dynamics.
The agent’s complete behavioral cycle integrates spatial movement with economic interaction:
def step(self):
"""Agent's action each step: move and trade"""
# Move to a random neighboring cell
possible_steps = self.model.grid.get_neighborhood(
self.pos, moore=True, include_center=False
)
new_position = self.random.choice(possible_steps)
self.model.grid.move_agent(self, new_position)
# Find agents in the same cell and trade with one
cellmates = self.model.grid.get_cell_list_contents([self.pos])
if len(cellmates) > 1:
other_agent = self.random.choice(cellmates)
if other_agent != self:
self.trade(other_agent)This coupling of mobility and trade opportunity creates a spatial dimension to economic interaction. Agents must co-locate to trade, introducing geographic constraints that mirror real economies where proximity facilitates exchange.
12.2 Measuring Inequality Through the Gini Coefficient
To quantify the inequality that emerges from random exchange, we employ the Gini coefficient G, a standard measure of distributional inequality. For a population of N agents with wealths W_1, W_2, …, W_N sorted in ascending order, the Gini coefficient is defined as:
G = (2Σ_{i=1}^N i·W_i)/(N·Σ_{i=1}^N W_i) - (N+1)/N
The Gini coefficient ranges from 0 (perfect equality, where all agents possess identical wealth) to 1 (perfect inequality, where one agent possesses all wealth). Values around 0.3-0.4 characterize relatively equal societies, while values exceeding 0.5 indicate extreme inequality. The computational implementation follows directly from this mathematical definition:
def compute_gini(self):
"""Calculate Gini coefficient for wealth inequality"""
agent_wealths = [agent.wealth for agent in self.agents]
x = sorted(agent_wealths)
n = len(x)
cumsum = sum(x)
if cumsum == 0:
return 0
return (2 * sum([(i+1) * xi for i, xi in enumerate(x)])) / (n * cumsum) - (n + 1) / nThe Gini coefficient connects intimately with the Lorenz curve, which plots cumulative wealth share against cumulative population share. For a perfectly equal distribution, the Lorenz curve follows the diagonal line L(p) = p, where p represents the population fraction. The Gini coefficient equals twice the area between the Lorenz curve and this equality line, providing a geometric interpretation of inequality.
12.3 Model Architecture and Data Collection
The WealthModel class orchestrates the simulation, managing agent creation, spatial distribution, and comprehensive data collection:
class WealthModel(mesa.Model):
def __init__(self, n_agents=100, width=10, height=10, initial_wealth=10):
super().__init__()
self.num_agents = n_agents
self.grid = mesa.space.MultiGrid(width, height, torus=True)
# Create agents with initial wealth
for i in range(self.num_agents):
agent = EconomicAgent(self, initial_wealth)
# Place agent randomly on grid
x = self.random.randrange(self.grid.width)
y = self.random.randrange(self.grid.height)
self.grid.place_agent(agent, (x, y))
# Data storage
self.wealth_history = []
self.gini_history = []
self.mean_history = []
self.max_history = []
self.min_history = []The data collection mechanism tracks multiple dimensions of the wealth distribution over time, capturing not just aggregate inequality but also the evolution of individual agent fortunes:
def collect_data(self):
"""Collect wealth data for analysis"""
wealths = [agent.wealth for agent in self.agents]
self.wealth_history.append(wealths)
self.gini_history.append(self.compute_gini())
self.mean_history.append(np.mean(wealths))
self.max_history.append(np.max(wealths))
self.min_history.append(np.min(wealths))
def step(self):
"""Advance model by one step"""
self.collect_data()
self.agents.shuffle_do("step")This comprehensive tracking enables detailed analysis of how inequality emerges and evolves, revealing temporal patterns that would be invisible from examining only initial and final states.
12.4 The Dynamics of Inequality Emergence
When we execute the simulation with 100 agents, each starting with 10 units of wealth, the results prove striking. Initially, the Gini coefficient sits at zero—perfect equality by construction. Within just a few time steps, inequality begins rising as random trades create variance in wealth holdings. Some agents gain through favorable random draws while others lose, and these initial differences compound over time.
The simulation reveals several distinct temporal phases. Early dynamics show rapid inequality growth as the initially uniform distribution spreads out. The Gini coefficient climbs steeply, reflecting how even small random perturbations can quickly generate meaningful disparities when compounded across multiple trading rounds. During this phase, the wealth distribution transitions from a delta function at the initial wealth to a broader distribution with increasing variance.
As the simulation progresses, the growth rate of inequality typically slows, and the system approaches a quasi-steady state. The Gini coefficient stabilizes around some equilibrium value, though it continues exhibiting stochastic fluctuations driven by ongoing random trades. This stabilization doesn’t imply that individual agent wealths remain constant—agents continue experiencing gains and losses—but rather that the overall distributional shape reaches a statistical equilibrium.
The final wealth distribution often exhibits characteristic features. A substantial fraction of agents accumulate near-zero wealth, having lost repeated unfavorable trades. A small number of fortunate agents accumulate much larger fortunes, sometimes holding wealth many times the original allocation. Between these extremes stretches a continuum of intermediate wealth levels, creating a right-skewed distribution where the mean exceeds the median.
12.5 Statistical Properties and Theoretical Insights
The wealth distribution emerging from random exchange exhibits mathematical properties that connect to fundamental results in probability theory. The model belongs to a class of systems known as exchange models or kinetic theory models of wealth, which draw analogies between economic exchanges and molecular collisions in statistical physics.
Under certain conditions, random exchange models converge to exponential wealth distributions in the long-time limit. For our bounded exchange rule, where transfers cannot exceed the minimum wealth of trading partners, the equilibrium distribution takes the form:
P(W) ∝ exp(-W/⟨W⟩)
where ⟨W⟩ denotes mean wealth. This exponential distribution arises from the principle of maximum entropy subject to the constraint of fixed total wealth—the most disordered distribution consistent with conservation laws. The exponential form implies that finding an agent with wealth substantially above the mean becomes exponentially unlikely, while agents clustered near zero wealth become common.
The Gini coefficient for an exponential distribution equals exactly 0.5, providing a theoretical prediction against which simulation results can be compared. Deviations from this value in simulations might reflect finite-size effects, insufficient equilibration time, or particular features of the spatial trading network that modify the statistical mechanics of exchange.
The absorbing boundary at zero wealth introduces additional complexity. Once agents reach zero wealth, they can receive but cannot give, effectively removing them from active trading. This creates a growing pool of effectively “inactive” agents who serve as wealth sinks, potentially accelerating inequality by concentrating remaining wealth among still-active traders.
12.6 Visualizing Inequality Through Multiple Lenses
The comprehensive visualization approach reveals different facets of inequality emergence. The final wealth histogram displays the distributional shape directly, showing the concentration of agents at low wealth levels and the long tail extending to high values. The mean wealth marker on this histogram highlights the gap between typical and average agent wealth—a hallmark of right-skewed distributions where a small number of extreme values pull the mean above the median.
The Gini coefficient trajectory over time provides a dynamic view of inequality growth, revealing whether the system exhibits monotonic convergence or more complex temporal patterns. Oscillations in the Gini trajectory might indicate that the system cycles through periods of consolidation and dispersion rather than settling into a stable equilibrium.
Tracking wealth statistics—mean, maximum, and minimum—over time exposes different aspects of distributional evolution. While the mean remains constant by conservation of total wealth, the maximum typically grows as fortunate agents accumulate windfalls, while the minimum decays toward zero as unlucky agents deplete their holdings. The divergence between these extreme values quantifies the spreading of the distribution.
The Lorenz curve provides perhaps the most intuitive visualization of inequality. By plotting cumulative wealth against cumulative population, it shows directly what fraction of total wealth the poorest X% of the population holds. The area between this curve and the equality line—visually represented in the shaded region—corresponds geometrically to the Gini coefficient, making inequality tangible through spatial representation.
12.7 Policy Implications and Model Extensions
The emergence of substantial inequality from fair, random exchange carries profound implications for understanding real economic systems. If significant inequality can arise even in the absence of systematic advantages, discrimination, or market failures, then inequality reduction might require active intervention rather than simply ensuring “fair” exchange rules.
This baseline model suggests several mechanisms that might mitigate or amplify inequality in practice. Redistribution policies that periodically transfer wealth from rich to poor agents could counteract the concentration tendency, maintaining lower inequality at the cost of interfering with exchange freedom. Progressive taxation schemes that extract larger fractions from wealthy agents might achieve similar effects more gradually.
Introducing agent heterogeneity adds realism and complexity. If agents differ in their propensity to trade, their risk tolerance, or their ability to identify favorable trading partners, these differences could generate inequality through mechanisms beyond pure randomness. Skilled traders might systematically extract value from less sophisticated partners, creating sustained inequality driven by ability differences rather than luck.
Network structure effects deserve particular attention. Our model implements random spatial encounters, but real economies exhibit complex network structures where some agents occupy central positions with many trading opportunities while others remain peripheral. Network centrality could become self-reinforcing—wealthy agents attract more trading partners, generating additional opportunities for wealth accumulation.
Savings and investment mechanisms could alter dynamics substantially. If agents could choose to withhold some wealth from risky trades or invest in productivity-enhancing activities, those with sufficient wealth might escape the zero-sum trading game and generate genuine growth. This could either amplify inequality by allowing the wealthy to grow faster or reduce it by providing escape routes from poverty traps.
12.8 Connections to Econophysics and Statistical Mechanics
The wealth distribution model exemplifies the emerging field of econophysics, which applies concepts and methods from statistical physics to economic phenomena. The analogy between trading agents and colliding gas molecules proves remarkably fruitful, with wealth playing the role of kinetic energy and trades corresponding to elastic collisions that conserve total energy.
This physical analogy provides theoretical tools for analyzing economic systems. The equilibrium wealth distribution emerges from considerations of entropy maximization, just as thermal equilibrium distributions in physics arise from statistical principles. The Boltzmann distribution of molecular energies finds its economic counterpart in the exponential wealth distribution, both reflecting maximum entropy subject to conservation constraints.
However, the analogy has limits. Physical particles lack agency and memory, while economic agents might learn from experience, form expectations, and strategically modify behavior. The simple random exchange model abstracts away these cognitive dimensions, treating agents as passive traders rather than strategic actors. More sophisticated models might endow agents with learning capabilities or allow them to remember past trading partners and adjust strategies accordingly.
The spatial dimension in our model introduces geographic structure absent from mean-field economic theories that assume all agents can trade with all others. Space creates local trading clusters and limits interaction range, potentially generating spatial patterns of wealth concentration. Wealthy neighborhoods might emerge through purely stochastic processes, even without explicit preferences for living near wealthy neighbors.
12.9 Computational Considerations and Simulation Design
From a computational perspective, the wealth model demonstrates several important simulation design principles. The modular separation between agent behavior and model coordination facilitates experimentation and modification. Changing trading rules, introducing heterogeneity, or implementing policy interventions requires modifying only specific components rather than restructuring the entire simulation.
The comprehensive data collection strategy enables multiple analytical approaches. Rather than committing to specific metrics in advance, collecting raw wealth distributions at each time step preserves maximum information for subsequent analysis. This flexibility proves valuable when unexpected patterns emerge during simulation—we can retroactively calculate new metrics or visualizations without re-running the entire simulation.
The complete simulation workflow integrates model initialization, execution, data collection, and visualization into a coherent pipeline:
model = WealthModel(n_agents=100, width=10, height=10, initial_wealth=10)
for i in range(100):
model.step()
model.collect_data()
# Analysis and visualization follow
final_wealth = np.array(model.wealth_history[-1])This structure makes the simulation reproducible and easily modified for parameter exploration. Researchers can sweep across parameter ranges—varying agent numbers, grid sizes, or initial wealth—to map out the parameter space and identify regions where qualitatively different behaviors emerge.
Performance optimization becomes important for large-scale simulations. While 100 agents on a 10×10 grid execute quickly, realistic economies might involve millions of agents or require thousands of time steps to reach equilibrium. Efficient data structures, vectorized operations, and possibly parallel processing become necessary for exploring such large-scale systems.
12.10 Interpreting Results and Avoiding Common Pitfalls
Interpreting simulation results requires care to avoid over-interpreting random fluctuations or mistaking artifacts for meaningful patterns. The stochastic nature of random exchange means that any single simulation run represents just one possible trajectory from the ensemble of possible outcomes. Running multiple replications with different random seeds reveals the range of typical behaviors and identifies robust patterns that persist across realizations.
The choice of simulation duration affects observed inequality levels. Too few time steps might show the system still evolving toward equilibrium, with inequality levels reflecting transient dynamics rather than stable outcomes. Conversely, excessively long simulations might reveal finite-size effects or pathological behaviors where all wealth concentrates in a single agent—an outcome that violates assumptions underlying continuum statistical theories.
Initial conditions can influence short-term dynamics even when they don’t affect long-term equilibria. Starting all agents at equal wealth ensures that observed inequality reflects only the trading dynamics rather than inherited disparities. Alternative initializations might explore how initial inequality levels affect subsequent evolution—do initially unequal societies converge toward the same equilibrium as initially equal ones, or do historical inequalities persist indefinitely?
The spatial dimension introduces additional interpretational challenges. Trading frequency depends on spatial density and mobility rates, with sparse grids or low mobility reducing encounter rates and slowing equilibration. The choice of grid size and agent density represents a trade-off between computational efficiency and spatial realism.
12.11 From Simple Models to Complex Realities
The wealth distribution model, like the random walk and Schelling models before it, demonstrates how simple rules generate complex outcomes. Fair random exchanges produce unfair outcomes. Individual-level equality yields population-level inequality. These paradoxes arise not from mathematical tricks but from fundamental properties of stochastic processes operating on systems with many interacting components.
Real economies involve vastly more complexity than our model captures. Production creates wealth rather than merely redistributing it. Human capital, technological innovation, institutional structures, and market imperfections all shape actual wealth distributions in ways our simple exchange model ignores. Yet the model’s very simplicity provides value precisely because it isolates one mechanism—random exchange—and reveals its inevitable consequences.
The emergence of inequality from random exchange suggests that achieving economic equality requires more than ensuring fair individual transactions. Even perfect procedural fairness at the micro level generates inequality at the macro level through the accumulation of random outcomes. This insight has profound implications for policy design—if inequality arises naturally from random processes, then maintaining equality requires active intervention rather than passive acceptance of “fair” market outcomes.
The progression from random walks through segregation to wealth inequality illustrates the power of agent-based modeling to illuminate social phenomena. Each model adds complexity—from aimless wandering to preference-driven sorting to economically motivated exchange—while maintaining the core principle that simple individual-level rules can generate complex system-level patterns. This modeling philosophy proves particularly valuable for social phenomena where controlled experiments prove impossible and direct observation reveals only outcomes rather than underlying mechanisms.
As we continue exploring agent-based approaches to social science, the wealth model reminds us that emergence operates across multiple domains. Spatial patterns emerge in the Schelling model, inequality emerges in the wealth model, and doubtless other surprising phenomena await discovery in models not yet constructed. The challenge lies in identifying which simple mechanisms generate which complex outcomes, building a repertoire of understood relationships between individual behaviors and collective consequences. Through systematic exploration of model spaces, we gradually map the landscape of possible social dynamics, revealing how the social world we observe arises from countless individual decisions made by boundedly rational agents navigating complex environments.
# Install Mesa if not already installed
import mesa
import random
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
# Economic Agent with trading capability
class EconomicAgent(mesa.Agent):
def __init__(self, model, initial_wealth):
super().__init__(model)
self.wealth = initial_wealth
def trade(self, other_agent):
"""Trade with another agent - transfers wealth randomly"""
if self.wealth > 0 and other_agent.wealth > 0:
trade_amount = min(self.wealth, other_agent.wealth)
transfer = self.random.randint(0, trade_amount)
self.wealth -= transfer
other_agent.wealth += transfer
def step(self):
"""Agent's action each step: move and trade"""
# Move to a random neighboring cell
possible_steps = self.model.grid.get_neighborhood(
self.pos, moore=True, include_center=False
)
new_position = self.random.choice(possible_steps)
self.model.grid.move_agent(self, new_position)
# Find agents in the same cell and trade with one
cellmates = self.model.grid.get_cell_list_contents([self.pos])
if len(cellmates) > 1:
other_agent = self.random.choice(cellmates)
if other_agent != self:
self.trade(other_agent)
# Wealth Distribution Model
class WealthModel(mesa.Model):
def __init__(self, n_agents=100, width=10, height=10, initial_wealth=10):
super().__init__()
self.num_agents = n_agents
self.grid = mesa.space.MultiGrid(width, height, torus=True)
# Create agents with initial wealth
for i in range(self.num_agents):
agent = EconomicAgent(self, initial_wealth)
# Place agent randomly on grid
x = self.random.randrange(self.grid.width)
y = self.random.randrange(self.grid.height)
self.grid.place_agent(agent, (x, y))
# Data storage
self.wealth_history = []
self.gini_history = []
self.mean_history = []
self.max_history = []
self.min_history = []
def compute_gini(self):
"""Calculate Gini coefficient for wealth inequality"""
agent_wealths = [agent.wealth for agent in self.agents]
x = sorted(agent_wealths)
n = len(x)
cumsum = sum(x)
if cumsum == 0:
return 0
return (2 * sum([(i+1) * xi for i, xi in enumerate(x)])) / (n * cumsum) - (n + 1) / n
def collect_data(self):
"""Collect wealth data for analysis"""
wealths = [agent.wealth for agent in self.agents]
self.wealth_history.append(wealths)
self.gini_history.append(self.compute_gini())
self.mean_history.append(np.mean(wealths))
self.max_history.append(np.max(wealths))
self.min_history.append(np.min(wealths))
def step(self):
"""Advance model by one step"""
self.collect_data()
self.agents.shuffle_do("step")
# Run simulation
print("Running Wealth Distribution Simulation...")
model = WealthModel(n_agents=100, width=10, height=10, initial_wealth=10)
for i in range(100):
model.step()
# Collect final data
model.collect_data()
# Get final wealth distribution
final_wealth = np.array(model.wealth_history[-1])
# Visualizations
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 1. Wealth distribution histogram (final state)
axes[0, 0].hist(final_wealth, bins=30, color='steelblue', edgecolor='black')
axes[0, 0].set_xlabel('Wealth')
axes[0, 0].set_ylabel('Number of Agents')
axes[0, 0].set_title('Final Wealth Distribution (Step 100)')
axes[0, 0].axvline(final_wealth.mean(), color='red', linestyle='--',
label=f'Mean: {final_wealth.mean():.2f}')
axes[0, 0].legend()
# 2. Gini coefficient over time
axes[0, 1].plot(range(len(model.gini_history)), model.gini_history,
linewidth=2, color='darkred')
axes[0, 1].set_xlabel('Step')
axes[0, 1].set_ylabel('Gini Coefficient')
axes[0, 1].set_title('Wealth Inequality Over Time')
axes[0, 1].grid(True, alpha=0.3)
# 3. Wealth statistics over time
steps = range(len(model.mean_history))
axes[1, 0].plot(steps, model.mean_history, label='Mean', linewidth=2)
axes[1, 0].plot(steps, model.max_history, label='Max', linewidth=2)
axes[1, 0].plot(steps, model.min_history, label='Min', linewidth=2)
axes[1, 0].set_xlabel('Step')
axes[1, 0].set_ylabel('Wealth')
axes[1, 0].set_title('Wealth Statistics Over Time')
axes[1, 0].legend()
axes[1, 0].grid(True, alpha=0.3)
# 4. Lorenz curve (final state)
sorted_wealth = sorted(final_wealth)
cumulative_wealth = [sum(sorted_wealth[:i+1]) for i in range(len(sorted_wealth))]
total_wealth = sum(sorted_wealth)
lorenz = [w / total_wealth for w in cumulative_wealth]
population_share = [(i+1) / len(sorted_wealth) for i in range(len(sorted_wealth))]
axes[1, 1].plot([0] + population_share, [0] + lorenz, linewidth=2, label='Lorenz Curve')
axes[1, 1].plot([0, 1], [0, 1], 'k--', label='Perfect Equality')
axes[1, 1].fill_between([0] + population_share, [0] + lorenz,
[0] + [p for p in population_share],
alpha=0.3, label='Gini Area')
axes[1, 1].set_xlabel('Cumulative Share of Population')
axes[1, 1].set_ylabel('Cumulative Share of Wealth')
axes[1, 1].set_title(f'Lorenz Curve (Gini: {model.gini_history[-1]:.3f})')
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Print summary statistics
print("\n" + "="*50)
print("SIMULATION SUMMARY")
print("="*50)
print(f"Number of agents: {model.num_agents}")
print(f"Initial wealth per agent: 10")
print(f"Total steps: 100")
print(f"\nFinal Statistics:")
print(f" Mean wealth: {final_wealth.mean():.2f}")
print(f" Median wealth: {np.median(final_wealth):.2f}")
print(f" Std deviation: {final_wealth.std():.2f}")
print(f" Min wealth: {final_wealth.min()}")
print(f" Max wealth: {final_wealth.max()}")
print(f" Final Gini coefficient: {model.gini_history[-1]:.3f}")
print(f" Initial Gini coefficient: {model.gini_history[0]:.3f}")
print("="*50)