@@ -20,7 +20,7 @@ kernelspec:
2020</div>
2121```
222223-# Racial Segregation
23+# Racial Segregation
24242525```{index} single: Schelling Segregation Model
2626```
@@ -191,30 +191,30 @@ class Agent:
191191 b = (self.location[1] - other.location[1])**2
192192 return sqrt(a + b)
193193194- def happy(self,
194+ def happy(self,
195195 agents, # List of other agents
196196 num_neighbors=10, # No. of agents viewed as neighbors
197197 require_same_type=5): # How many neighbors must be same type
198198 """
199199 True if a sufficient number of nearest neighbors are of the same
200- type.
200+ type.
201201 """
202202203203 distances = []
204-204+205205 # Distances is a list of pairs (d, agent), where d is distance from
206206 # agent to self
207207 for agent in agents:
208208 if self != agent:
209209 distance = self.get_distance(agent)
210210 distances.append((distance, agent))
211-211+212212 # Sort from smallest to largest, according to distance
213213 distances.sort()
214-214+215215 # Extract the neighboring agents
216216 neighbors = [agent for d, agent in distances[:num_neighbors]]
217-217+218218 # Count how many neighbors have the same type as self
219219 num_same_type = sum(self.type == agent.type for agent in neighbors)
220220 return num_same_type >= require_same_type
@@ -248,9 +248,9 @@ def plot_distribution(agents, cycle_num):
248248 fig, ax = plt.subplots()
249249 plot_args = {'markersize': 8, 'alpha': 0.8}
250250 ax.set_facecolor('azure')
251- ax.plot(x_values_0, y_values_0,
251+ ax.plot(x_values_0, y_values_0,
252252 'o', markerfacecolor='orange', **plot_args)
253- ax.plot(x_values_1, y_values_1,
253+ ax.plot(x_values_1, y_values_1,
254254 'o', markerfacecolor='green', **plot_args)
255255 ax.set_title(f'Cycle {cycle_num-1}')
256256 plt.show()
@@ -274,24 +274,24 @@ The real code is below
274274```{code-cell} ipython3
275275def run_simulation(num_of_type_0=600,
276276 num_of_type_1=600,
277- max_iter=100_000, # Maximum number of iterations
278- set_seed=1234):
277+ max_iter=100_000, # Maximum number of iterations
278+ set_seed=1234):
279279280280 # Set the seed for reproducibility
281- seed(set_seed)
282-281+ seed(set_seed)
282+283283 # Create a list of agents of type 0
284284 agents = [Agent(0) for i in range(num_of_type_0)]
285285 # Append a list of agents of type 1
286286 agents.extend(Agent(1) for i in range(num_of_type_1))
287287288288 # Initialize a counter
289289 count = 1
290-290+291291 # Plot the initial distribution
292292 plot_distribution(agents, count)
293-294- # Loop until no agent wishes to move
293+294+ # Loop until no agent wishes to move
295295 while count < max_iter:
296296 print('Entering loop ', count)
297297 count += 1
@@ -303,15 +303,15 @@ def run_simulation(num_of_type_0=600,
303303 no_one_moved = False
304304 if no_one_moved:
305305 break
306-306+307307 # Plot final distribution
308308 plot_distribution(agents, count)
309309310310 if count < max_iter:
311311 print(f'Converged after {count} iterations.')
312312 else:
313313 print('Hit iteration bound and terminated.')
314-314+315315```
316316317317Let's have a look at the results.
@@ -346,7 +346,7 @@ The object oriented style that we used for coding above is neat but harder to
346346optimize than procedural code (i.e., code based around functions rather than
347347objects and methods).
348348349-Try writing a new version of the model that stores
349+Try writing a new version of the model that stores
350350351351* the locations of all agents as a 2D NumPy array of floats.
352352* the types of all agents as a flat NumPy array of integers.
@@ -375,7 +375,6 @@ solution here
375375376376```{code-cell} ipython3
377377from numpy.random import uniform, randint
378-from numba import njit
379378380379n = 1000 # number of agents (agents = 0, ..., n-1)
381380k = 10 # number of agents regarded as neighbors
@@ -386,13 +385,10 @@ def initialize_state():
386385 types = randint(0, high=2, size=n) # label zero or one
387386 return locations, types
388387389-@njit # Use Numba to accelerate computation
388+390389def compute_distances_from_loc(loc, locations):
391- " Compute distance from location loc to all other points. "
392- distances = np.empty(n)
393- for j in range(n):
394- distances[j] = np.linalg.norm(loc - locations[j, :])
395- return distances
390+ """ Compute distance from location loc to all other points. """
391+ return np.linalg.norm(loc - locations, axis=1)
396392397393def get_neighbors(loc, locations):
398394 " Get all neighbors of a given location. "
@@ -417,7 +413,7 @@ def count_happy(locations, types):
417413 for i in range(n):
418414 happy_sum += is_happy(i, locations, types)
419415 return happy_sum
420-416+421417def update_agent(i, locations, types):
422418 " Move agent if unhappy. "
423419 moved = False
@@ -432,11 +428,11 @@ def plot_distribution(locations, types, title, savepdf=False):
432428 colors = 'orange', 'green'
433429 for agent_type, color in zip((0, 1), colors):
434430 idx = (types == agent_type)
435- ax.plot(locations[idx, 0],
436- locations[idx, 1],
437- 'o',
431+ ax.plot(locations[idx, 0],
432+ locations[idx, 1],
433+ 'o',
438434 markersize=8,
439- markerfacecolor=color,
435+ markerfacecolor=color,
440436 alpha=0.8)
441437 ax.set_title(title)
442438 plt.show()
@@ -458,15 +454,15 @@ def sim_random_select(max_iter=100_000, flip_prob=0.01, test_freq=10_000):
458454 i = randint(0, n)
459455 moved = update_agent(i, locations, types)
460456461- if flip_prob > 0:
457+ if flip_prob > 0:
462458 # flip agent i's type with probability epsilon
463459 U = uniform()
464460 if U < flip_prob:
465461 current_type = types[i]
466462 types[i] = 0 if current_type == 1 else 1
467463468464 # Every so many updates, plot and test for convergence
469- if current_iter % test_freq == 0:
465+ if current_iter % test_freq == 0:
470466 cycle = current_iter / n
471467 plot_distribution(locations, types, f'iteration {current_iter}')
472468 if count_happy(locations, types) == n: