schelling#

  • Series: lecture-python-intro

  • File: lectures/schelling.md

  • Audit date: 2026-08-26

  • Corpus snapshot: a12d17c0ef

  • Categories audited: writing, math, code, figures, references, links, admonitions (JAX out of scope)

  • Overall score: 8.6 / 10

  • Priority: NONE

Score breakdown#

Category

Score

One-line note

Writing

5/10

qe-writing-005 ×4; qe-writing-003 ×4; qe-writing-002 ×4, +3 more.

Math

10/10

no mechanical violations detected.

Code

7/10

qe-code-001 ×13.

JAX

out of scope

JAX rules target lecture-jax.

Figures

9/10

qe-fig-003 ×1.

References

10/10

no mechanical violations detected.

Links

9/10

qe-link-002 ×1.

Admonitions

10/10

no mechanical violations detected.

Issues#

Critical#

None found.

High severity#

  • [qe-code-001] (reviewer) — Follow PEP8 unless closer to mathematical notation. Count: 13. Lines: 176, 190, 247, 310, 379, 421, 464. Example: 176 names a parameter type, shadowing the builtin, and the attribute self.type then propagates it through happy (214) and plot_distribution (237); the exercise version gets this right with agent_type (425). 190-192 over-indent the three continuation lines of def happy(self, relative to the opening delimiter, and 247 and 249 under-indent the continuations of the two ax.plot calls. 310 leaves a blank line at the end of the run_simulation cell. Seven top-level defs in the exercise cell are separated by a single blank line (379, 389, 396, 406, 413, 421, 438) while one pair (382-384) has two, so the file is inconsistent with itself. Three further readability items: plot_distribution is redefined at 421 with an incompatible signature, shadowing the version at 230, and carries the older version’s docstring (“after cycle_num rounds of the loop”) although its parameter is now title; its savepdf=False argument is never used; and cycle = current_iter / n at 464 is computed and discarded. for i in range(num_of_type_0) at 281 and 283 binds a loop variable that is never read.

  • [qe-writing-006] — Capitalize lecture titles properly. Count: 1. Lines: 79. Example: H3 Title Case: ‘Set-Up’ (Set-Up).

Medium severity#

  • [qe-fig-003] — No matplotlib embedded titles. Count: 1. Lines: 250. Example: .set_title.

  • [qe-link-002] — Use doc links for cross-series references. Count: 1. Lines: 154. Example: raw link to python-programming.quantecon.org.

  • [qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 4. Lines: 56, 60, 81, 327. Example: the central result is stated four times in the Outline and three more times after the simulation. 47-48 (“mixed race neighborhoods are likely to be unstable, tending to collapse over time”), 50-51 (“the model predicts strongly divided neighborhoods, with high levels of segregation”) and 53-54 (“extreme segregation outcomes arise even though people’s preferences are not particularly extreme”) are three formulations of one claim in eight lines; 327-331 then repeats it three more times (“how rapidly racial integration breaks down” / “people in the model don’t actually mind living mixed with the other type” / “Even with these preferences, the outcome is a high degree of segregation”). 60 (“These ideas will become clearer as the lecture unfolds”) carries nothing. 56-58 says “in the model” twice in one sentence. And 81-83 restates the heading at 75 and the sentence at 77 in a 26-word chain of three coordinated clauses (“different from the original but also easy to program and, at the same time, captures his main idea”).

  • [qe-writing-003] (reviewer) — Maintain logical flow. Count: 4. Lines: 247, 250, 370, 476. Example: 370 is a placeholder that reached publication: the {solution-start} block for schelling_ex1 opens with the single line solution here before the code cell. Second, the model is described entirely in colours and the figures are labelled entirely in integers. 85 sets up “two types of people: orange people and green people”, 226-227 says “Orange agents are represented by orange dots and green ones are represented by green dots”, and the legend then reads Type 0 and Type 1 (247, 249, and again at 433 in the exercise), so the reader must map the words onto the picture unaided. Third, the figure title and the printed message disagree by one: plot_distribution titles the plot f'Cycle {cycle_num-1}' (250) while run_simulation passes the same count to print(f'Converged after {count} iterations.') (307), so the final figure is captioned one cycle behind the sentence beneath it. Fourth, the exercise’s second modification is implemented and then never discussed: 357 asks for the type to flip with probability 0.01, 359-362 motivates it (“every so often, an agent moves to a different city and, with small probability, is replaced by an agent of the other type”), the solution implements it at 455-460 and runs it at 481 - and 476 concludes only that “we again find that mixed neighborhoods break down and segregation emerges”, which was already true without the flip. What the perturbation changes (the state can no longer settle permanently, so count_happy(locations, types) == n at 466 need never hold and the run ends at the iteration bound) is left for the reader to infer from a printed message the text does not mention.

  • [qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 4. Lines: 101, 103, 110, 112. Example: the two definitions the entire model rests on are set in italic rather than bold: “We will say that an agent is happy if 5 or more of her 10 nearest neighbors are of the same type” (101) and “An agent who is not happy is called unhappy” (103) - both are definitions on first use, and the lecture’s only correctly bolded definition, unit square (94), is seven lines above them. 110 introduces a third term in single quotes, “‘Nearest’ is in terms of [Euclidean distance]”, where the lecture uses double quotes for scare quotes elsewhere (“surrounded” at 45, “address” at 91). And 112 uses bold for emphasis - “agents are not averse to living in mixed areas” - where italic is wanted; interactions at 56 shows the lecture knows the italic form.

  • [qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 2. Lines: 289, 321. Example: the lecture’s claims are about a rate and its figures show only endpoints. run_simulation plots the distribution once before the loop (289) and once after it (304), and the prose then makes three assertions about the path between them - “after several cycles, they become segregated into distinct regions” (321), “the program terminated after a small number of cycles” (323), and “What is striking about the pictures is how rapidly racial integration breaks down” (327) - with no intermediate figure to support any of them. The exercise version does the right thing, plotting inside the loop every test_freq iterations (463-465), so the main text is the weaker of the two presentations of the same model. Second, nothing quantifies segregation at any point. count_happy (406-411) exists in the exercise and is used only as a convergence test; a curve of the fraction of happy agents, or of the average share of same-type neighbours, against cycle number would turn “rapidly” into a measurement and would let 327-331 point at something.

Low severity#

  • [qe-writing-008] — Remove excessive whitespace between words. Count: 1. Lines: 91. Example: 2 spaces.

Strengths#

  • The two {prf:algorithm} directives state the mechanics before any code appears - the three-step move rule (134-141) and the four-step main loop (258-267) - and each is followed by the code that implements it, so the reader checks an implementation against a specification rather than reading code for the specification.

  • The model’s crucial and counterintuitive feature is stated explicitly and then repeated where it matters: agents “are not averse to living in mixed areas” and “are perfectly happy if half of their neighbors are of the other color” (112-114), which is what makes the result at 327-331 surprising rather than mechanical.

  • The class skeleton is given as a plain data-and-methods sketch in a {code-block} none (158-169) before the actual class, which is the right way to introduce an object to readers who have just met objects - and 154 links out to the OOP lecture for those who have not.

  • Agent.happy (189-215) is commented at exactly the granularity a first-course reader needs - one comment per step, naming what the step produces (“Distances is a list of pairs (d, agent)”, “Sort from smallest to largest, according to distance”, “Extract the neighboring agents”, “Count how many neighbors have the same type as self”) - and its three thresholds are keyword arguments with inline explanations rather than magic numbers.

  • run_simulation is fully reproducible and says so: set_seed=1234 is a named argument, seed(set_seed) is called with a comment giving the reason (277-278), and the loop carries an explicit max_iter bound with a distinct message for each exit path (306-309).

  • The exercise is genuinely instructive rather than decorative: it asks for the same model rewritten in array-based procedural style, states the pedagogical reason (“neat but harder to optimize than procedural code”, 341-343), specifies the two data structures, and then adds two behavioural changes so the rewrite is not a transcription.

  • The exercise solution’s vectorised distance computation (385-394) replaces the class version’s Python loop and list sort with np.linalg.norm(..., axis=1) and np.argsort, and the two are close enough side by side that the reader can see what vectorisation replaced.