quant-econ Solutions: Schelling's Segregation Model

Solutions for http://quant-econ.net/jl/schelling.html

Exercise 1

Here's one solution that does the job we want. If you feel like a further exercise you can probably speed up some of the computations and then increase the number of agents.


In [5]:
using PyPlot
srand(42)  # set seed for random numbers. Reproducible output

In [2]:
type Agent
    kind::Int
    location::Vector{Float64}
end


# constructor
Agent(k::Int) = Agent(k, rand(2))


function draw_location!(a::Agent)
    a.location = rand(2)
    nothing
end

# Define subtraction as difference between locations
-(a::Agent, o::Agent) = a.location - o.location

# distance is just 2 norm: uses our subtraction function
get_distance(a::Agent, o::Agent) = norm(a - o)


function is_happy(a::Agent, others::Vector{Agent})
    "True if sufficient number of nearest neighbors are of the same type."
    # distances is a list of pairs (d, agent), where d is distance from
    # agent to self
    distances = Any[]
    
    for agent in others
        if a != agent
            dist = get_distance(a, agent)
            push!(distances, (dist, agent))
        end
    end
    
    # == Sort from smallest to largest, according to distance == #
    sort!(distances)
    
    # == Extract the neighboring agents == #
    neighbors = [agent for (d, agent) in distances[1:num_neighbors]]
    
    # == Count how many neighbors have the same type as self == #
    num_same_type = sum([a.kind == other.kind for other in neighbors])
    
    return num_same_type >= require_same_type
end

function update!(a::Agent, others::Vector{Agent})
    "If not happy, then randomly choose new locations until happy."
    while !is_happy(a, others)
        draw_location!(a)
    end
    return nothing
end


function plot_distribution(agents::Vector{Agent}, cycle_num)
    x_vals_0, y_vals_0 = Float64[], Float64[]
    x_vals_1, y_vals_1 = Float64[], Float64[]

    # == Obtain locations of each type == #
    for agent in agents
        x, y = agent.location
        if agent.kind == 0
            push!(x_vals_0, x)
            push!(y_vals_0, y)
        else
            push!(x_vals_1, x)
            push!(y_vals_1, y)
        end
    end
    
    fig, ax = subplots(figsize=(6, 5))
    plot_args = {:markersize => 8.0, :alpha => 0.6}
    ax[:set_axis_bgcolor]("azure")
    ax[:plot](x_vals_0, y_vals_0, "o", markerfacecolor="orange";  plot_args...)
    ax[:plot](x_vals_1, y_vals_1, "o", markerfacecolor="green"; plot_args...)
    ax[:set_title]("Cycle $(cycle_num)")
    
    return nothing
end;

In [3]:
# == Main == #

num_of_type_0 = 250
num_of_type_1 = 250
num_neighbors = 10      # Number of agents regarded as neighbors
require_same_type = 7   # Want at least this many neighbors to be same type

# == Create a list of agents == #
agents = Agent[Agent(0) for i in 1:num_of_type_0]
push!(agents, [Agent(1) for i in 1:num_of_type_1]...)

count = 1

# ==  Loop until none wishes to move == #
while true
    println("Entering loop $count")
    plot_distribution(agents, count)
    count += 1
    no_one_moved = true
    movers = 0
    for agent in agents
        old_location = agent.location
        update!(agent, agents)
        if !isapprox(0.0, maximum(old_location - agent.location))
            no_one_moved = false
        end
    end
    if no_one_moved
        break
    end
end

println("Converged, terminating")


Entering loop 1
Entering loop 2
Entering loop 3
Entering loop 4
Converged, terminating