We want you to be able to:
In 1974, economist Thomas Schelling published what would become a landmark paper called "Dynamic Models of Segregation." In it, Schelling tried to explore how, in a simple toy system, individuals segregate themselves even if they have an expressed preference for having neighbors different from themselves. In other words: Schelling explored how a seemingly sensible individual preference ("I'm OK with up to 50% of the people in my neighborhood being different from me") can lead to larger-scale social segregation, where 'like' people clump together.")
Now, Schelling wrote this paper in 1974, in a different cultural context. You'll find that,
Studying this model and the history involved means also accepting the baggage that surrounds them. These models offer us great insight into social systems, but they also have a history and implications that touch on tense social issues. So, as a class, let's:
His first toy system looked like this: a one-dimensional set of typewriter characters to stand in for individuals who differed on some property ("stars and zeros"):
Schelling describes the figure thusly:
Suppose, now, that everybody wants at least half his neighbors to be like himself, and that everyone defines 'his neighborhood' to include the four nearest neighbors on either side of him. A star wants at least four of his eight nearest neighbors to be stars; a zero wants at least four of his eight nearest neighbors to be zeros. Including himself, this means that he wants a bare majority, five out of the nine. (For those near the end of the line the rule is that, of the four neighbors on the side toward the center plus the one, two or three outboard neighbors, half must be like oneself) I have put a dot over each individual whose neighborhood does not meet his demands.
Let's look at the line again. We'll take two individual characters (two zeroes) as examples. The first zero doesn't want to move, because its neighborhood has a simple majority of zeroes like itself. But what about the zero that lives next door?
// Calculate the number of residents in the second zero's neighborhood to figure out if it wants to move.
The results of this section are experimental. They are crude and abstract but have the advantage that anyone can reproduce them using materials that are readily available.
I assume a population exhaustively divided into two groups; everyone's membership is permanent and recognizable. Everybody is assumed to care about the color of the people he lives among and able to observe the number of blacks and whites that occupy a piece of territory. Everybody has a particular location at any moment; and everybody is capable of moving if [they are] dissatisfied with the color mixture where [they are]. The numbers of blacks and whites, their color preferences, and the sizes of 'neighborhoods' will be manipulated....
There is some fascination in the process as it emerges in the linear model; furthermore, the linear experiment can be replicated by any reader in five minutes; variants can readily be devised, and any reader with a spare half hour can change the hypotheses to suit.
Here's how Schelling sets up the rules for a 1-dimensional simulation:
As Schelling says, "We also need an order of moving."
When Schelling did his original simulations in 1974, he wrote "what is reported here has all been done by hand and eye." If he could do it, so can we.
Grab nickels and pennies and find a small group (there probably aren't enough to do it solo or in pairs). We're going to try this in stages.
Now that you're set up, play one round of the game.
When does/should the round stop? In other words, how would you describe how you know the round is over. Try and be as precise as you can in explaining.
If you can, take a picture of what your board looks like after one round and upload it with your notebook!
This gives you a chance to talk about whether anything problematic or unexpected happened.
Play a second round through. And third, and fourth if you want. Set up a new board and try again, configuring the initial board how you want.
// If you can, take a picture of what your board starts like, looks like after each round and ends like in each game.
You've played the game. You hopefully have a handle on it. Now, let's try to move closer toward describing the model in a way that a computer - not just you - could potentially play/run the game.
You do not yet need to make a working Python program! We're going to start much smaller and try to build up. Using the whiteboards in class, and through words, pictures, flowcharts, or something else, start precisely describing the game so a non-human agent could play it.
One strategy you can use is often called top-down design. You start at the highest level of what you're trying to do, then break each big piece down into smaller, more specific pieces (thinking about specific pieces as functions, loops, or individual important pieces of code). You can also write in a way that looks like code, but doesn't have to be strict python (or any other language). Just something akin to a program sketch: understandable as a well-defined series of steps, even if it isn't Python code.
It takes practice, and you may hit dead-ends. But when you're done, you ought to have a set of instructions that:
In the cell below, describe the basic things that you want to do. Or, alternately, take pictures of your whiteboard and upload them along with this notebook!
Put your pseudo-code here! (Or upload pictures, but make sure to make a note here.)
This problem is going to be based on a few key pieces of code, which we're going to implement as individual components. In the space below, we'll ask you to implement these pieces of code as functions, and to write code that shows these functions are behaving correctly.
Function 1: Write a function that creates a one-dimensional game board composed of agents of two different types (0 and 1, X and O, stars and pluses... whatever you want), where the agents are assigned to spots randomly with a 50% chance of being either type. As arguments to the function, take in (1) the number of spots in the game board (setting the default to 32) and (2) a random seed that you will use to initialize the board (again with some default number), and return your game board. (Hint: which makes more sense to describe the game board, a list or a Numpy array? What are the tradeoffs?) Show that your function is behaving correctly by printing out the returned game board.
In [ ]:
# Put your code here!
import random
import math
def initialize_list(array_size=32, randseed=8675309):
'''
This function optionally takes in an array size and random seed
and returns the initial neighborhood that we're going to start
from - a string of zeros and ones. If no arguments are given, it
defaults to the values specified.
'''
random.seed(randseed)
initial_list = []
for i in range(array_size):
initial_list.append(random.randint(0,1))
return initial_list
def neighborhood_print(neighborhood, note=''):
'''
This is a convenience function to take our neighborhood list,
make a string of stars and zeros out of it, and print the string
plus optional text at the end. It's not necessary but it looks pretty.
'''
neighborstring=''
for i in range(len(neighborhood)):
if(neighborhood[i]) > 0:
neighborstring += '*'
else:
neighborstring += '0'
# make sure optional text is a string
if type(note)!=str:
note = str(note)
# add an extra space to make it look nice!
if note != '':
note = ' ' + note
neighborstring += note
print(neighborstring)
my_board = initialize_list()
neighborhood_print(my_board)
Function 2: Write a function that takes the game board generated by the function you wrote above and determines whether an agent at position i in the game board of a specified type is happy for a game board of any size and a neighborhood of size N (i.e., from position i-N to i+N), and returns that information. Make sure to check that position i is actually inside the game board (i.e., make sure the request makes sense), and ensure that it behaves correctly for agents near the edges of the game board. Show that your function is behaving correctly by giving having it check every position in the game board you generated previously, and decide whether the agent in each spot is happy or not. Verify by eye that it's behaving correctly. (Hint: You're going to use this later, when you're trying to decide where to put an agent. Should you write the function assuming that the agent is already in the board, or that you're testing to see whether or not you've trying to decide whether to put it there?)
In [ ]:
# Put your code here, using additional cells if necessary.
def is_happy(my_list, my_value, my_index):
'''
This function assumes that my_list has a value (my_value)
popped out of it already, and checkes to see if my_value
would be happy in my_list at index my_index. It returns
'True' if happy and 'False' if unhappy under those circumstances.
'''
# do some error-checking (is the index within the allowed range?)
if my_index < 0 or my_index > len(my_list):
print("you've made an indexing error!", my_index)
start = my_index-4 # start 4 to the left
end = my_index+4 # end 3 to the right b/c we count the value at my_index too
# if the starting value is out of bounds, fix it
if start < 0:
start = 0
# if the ending value is out of bounds, fix it. note that we want to go to
# len(list), not len(list)-1, because range() goes to 1 before the end of
# the range!
if end > len(my_list):
end = len(my_list)
# keep track of the neighbors that are like me
neighbors_like_me = 0
# keep track of total neighbors
total_neighbors = 0
# loop over the specified range
for i in range(start,end):
if my_list[i] == my_value: # if this neighbor is like me, keep track of that
neighbors_like_me += 1
total_neighbors+=1 # also keep track of total neighbors
# happy if at least half are like me, unhappy otherwise
# note: it's *at least* half because we're not double-counting our
# own value
if neighbors_like_me/total_neighbors >= 0.5:
return True
else:
return False
In [ ]:
my_board = initialize_list()
neighborhood_print(my_board)
for i in range(len(my_board)):
agent = my_board.pop(i)
am_i_happy = is_happy(my_board, agent, i)
my_board.insert(i, agent)
if am_i_happy==True:
print("agent {} at position {} is HAPPY! :-)".format(agent,i))
else:
print("agent {} at position {} is UNHAPPY! :-(".format(agent,i))
In the space below, create a program that plays the whole game, from start to finish. Make sure to use the two functions that you just created, and for every step in the game print out the position you're looking at and the game board to verify that it is behaving correctly!
In [ ]:
# Put your code here, using additional cells if necessary.
def where_to_move(my_list, my_value, my_index):
'''
Given a neighborhood (my_list), a value (my_value), and the index
that it started at (my_index), figure out where to move my_value
so that it's happy. This assumes that my_value is unhappy where it
is, by the way! This function then returns the index where my_value
should move to in order to be happy.
'''
# this block of code steps to the left to see where (if anywhere) it's
# happy to the left. If it continues to be unhappy, it'll stop when it
# is about to step off the end of the list.
left_index=my_index-1
left_happy=False
while left_happy==False and left_index >= 0:
left_happy = is_happy(my_list,my_value,left_index)
if left_happy==False:
left_index -= 1
# as above, but to the right.
right_index=my_index+1
right_happy=False
while right_happy==False and right_index < len(my_list):
right_happy = is_happy(my_list,my_value,right_index)
if right_happy==False:
right_index += 1
# now we figure out where the new index should be!
if left_index < 0 and right_index < len(my_list):
# can't be happy to the left; only possible answer is right_index
new_index = right_index
elif left_index >= 0 and right_index > len(my_list):
# can't be happy to the right; only possible answer is left_index
new_index = left_index
elif left_index >= 0 and right_index <= len(my_list):
# we're within bounds, so now check to see which side is closer.
# if they're the same we move it to the left. (This was never specified
# by Schelling, so we have to make a choice on that.)
if math.fabs(left_index-my_index) > math.fabs(right_index-my_index):
new_index = right_index
else:
new_index = left_index
else:
# this should only ever be called if something goes horribly wrong.
print("something has gone wrong in where_to_move!")
return new_index;
In [ ]:
# initialize list to defaults
neighborhood = initialize_list(array_size=32)
neighborhood_print(neighborhood, 'initial state')
# do 2 loops over the list
for i in range(2):
this_index = 0
# step through the neighborhood once
while this_index < len(neighborhood):
this_val = neighborhood.pop(this_index)
if is_happy(neighborhood,this_val,this_index):
# if we're happy where we are, don't change anything!
neighborhood.insert(this_index,this_val)
else:
# we're unhappy; we need to figure out where to move and then move.
new_index = where_to_move(neighborhood,this_val,this_index)
neighborhood.insert(new_index,this_val)
neighborhood_print(neighborhood, this_index)
# increment this_index or we'll never stop looping
this_index += 1
# print out the final state, just to see what it's like.
neighborhood_print(neighborhood, 'final state!')
In [ ]:
%matplotlib inline
import matplotlib.pyplot as plt
plt.plot(neighborhood,'ro')
plt.xlim(-2,len(neighborhood)+1)
plt.ylim(-0.1,1.1)
In [ ]:
from IPython.display import HTML
HTML(
"""
<iframe
src="https://goo.gl/forms/c82cM9qmjBfI9DM72?embedded=true"
width="80%"
height="1200px"
frameborder="0"
marginheight="0"
marginwidth="0">
Loading...
</iframe>
"""
)