odu#

  • Series: lecture-dp

  • File: lectures/odu.md

  • Audit date: 2026-08-26

  • Corpus snapshot: c30490a2f4

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

  • Overall score: 7.4 / 10

  • Priority: HIGH

Score breakdown#

Category

Score

One-line note

Writing

3/10

qe-writing-006 ×7; qe-writing-002 ×5; qe-writing-005 ×2, +4 more.

Math

9/10

qe-math-015 (proposed) ×4; qe-math-009 ×3.

Code

7.5/10

qe-code-001 ×6.

JAX

out of scope

JAX rules target lecture-jax.

Figures

5/10

qe-fig-005 ×5; qe-fig-003 ×3; qe-fig-006 ×1, +2 more.

References

9/10

qe-ref-001 ×1.

Links

8/10

qe-link-002 ×4.

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: 6. Lines: 371, 970, 799, 833, 311, 1009. Example: 371 opens the docstring of get_greedy with four quotation marks, """", so the docstring begins with a stray " character. 970-972 assign lambdas to names (l = lambda w: f(w) / g(w), obj = lambda w: l(w) - 1.) - E731, and l is the identifier PEP8 singles out as ambiguous (E741); the same pattern appears at 230-231, 302-303 and 793. 799 tests if e == False: where if not e: is the PEP8 form (E712). 833 binds p = np.random.uniform(0, 1) inside a jitted function while p is the beta density defined at module scope (224) and called as a function at 913 - and 642 does the same again with def ω_func(p, ω), so p is a density, a float and a probability grid point in one lecture. 311-312 draw the Monte Carlo integration nodes with np.random.beta and no seed, as do 800, 912 and 833, so every figure in the lecture - and the convergence counts printed at 737 and 743 - change on each build. 1009 assigns the quiver artist to q, the symbol the model uses for the unknown density (94, 123, 184), and never uses it (F841). Also 441-442 are two blank lines before a return inside a function (E303), and 1002-1007 allocates ΔW with np.zeros and ΔΠ with np.empty but fills only ΔΠ, which is deliberate (the arrows are horizontal) and worth one comment.

  • [qe-fig-001] — Do not set figure size unless necessary. Count: 7. Lines: 233, 451, 466, 754, 846, 981, 1041. Example: figsize=.

  • [qe-fig-005] — Descriptive figure names for cross-referencing. Count: 5. Lines: 222, 448, 462, 750, 782. Example: code-cell figure without mystnb figure metadata.

  • [qe-fig-008] — Use lw=2 for line charts. Count: 9. Lines: 756, 847, 993, 1022, 1023, 1043, 1044, 1050, 1051. Example: plot() without lw=.

  • [qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 5. Lines: 94, 265, 129, 27, 1159. Example: 94 and 133 write the wage sequence as ${W_t}$ with the braces unescaped, so the set notation vanishes and the source renders as \(W_t\) - the same object is written correctly as $\{W_t\}$ nowhere in the file. 265 is worse: $\mathbb 1{w\geq \bar w(\pi) }$ has both an unbraced \mathbb 1 and unescaped set braces, in the sentence that states the lecture’s central conjecture (‘Summary: We conjecture that the optimal policy is of the form …’), and line 113 writes the same indicator correctly as $\mathbf{1}\{w \geq \bar w\}$ - so the file has two spellings of one symbol and the broken one is in the summary. Then five typos: ‘repectively’ (129), ‘the the distribution’ (149), ‘accpet_t’ (892), ‘currently employment’ (832), ‘unemployment compensation if low’ (1159), plus ‘priori’ for ‘prior’ twice (906, 1088). And 27 says ‘this lecture deploys the libraries:’ - plural, above a cell that installs one.

  • [qe-writing-006] — Capitalize lecture titles properly. Count: 7. Lines: 70, 82, 116, 242, 484, 499, 1064. Example: H3 Title Case: ‘Model Features’ (Features).

  • [qe-writing-008] — Remove excessive whitespace between words. Count: 23. Lines: 42, 102, 129, 131, 139, 149, 260, 863, 944, 945, …. Example: 2 spaces.

Medium severity#

  • [qe-fig-003] — No matplotlib embedded titles. Count: 3. Lines: 850, 1047, 1054. Example: .set_title.

  • [qe-link-002] — Use doc links for cross-series references. Count: 4. Lines: 38, 41, 861, 1071. Example: raw link to python.quantecon.org.

  • [qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 3. Lines: 590, 561, 1066. Example: 590 introduces two symbols that are already taken, inside the contraction proof: ‘for real numbers \(a, b, c\) we always have’, where \(c\) is unemployment compensation throughout the lecture (218, 542, 567) and appears in {eq}`odu_dq` twelve lines above, and \(b\) is the space of bounded functions declared at 558 (‘\(b[0,1]\) be the bounded real-valued functions on \([0,1]\)’). Renaming the three dummies in {eq}`odu_nt2` costs nothing and removes both collisions. 561 names the operator \(Q\) in a lecture whose unknown object is the density \(q\) (94, 123) and whose mixture density is \(q_\pi\) (184) - three closely related meanings on one letter, and the code then binds q to a matplotlib artist at 1009. 1066, 1123, 1140, 1152 and 1178 write the distributional statements with a literal tilde outside math, ‘\(F\) ~ Beta(1, 1), \(G\) ~ Beta(3, 1.2)’, where $F \sim \operatorname{Beta}(1, 1)$ is both correct and what 216-217 already uses for \operatorname{Beta}.

  • [qe-math-015 (proposed)] (reviewer) — Lowercase for densities/PMFs, uppercase for CDFs. Count: 4. Lines: 1022, 216, 256, 1096. Example: the lecture states the convention correctly and then breaks it in both directions. 127-129: ‘The worker knows there are two possible distributions \(F\) and \(G\). / These two distributions have densities \(f\) and \(g\), repectively.’ - uppercase for the distributions, lowercase for the densities, exactly as the rule asks. But 1022-1023 plot 1 - beta.cdf(w_bar, F_a, F_b) and 1 - beta.cdf(w_bar, G_a, G_b) under the legend labels '$f$' and '$g$', i.e. a quantity computed from the CDF carries the density letter - and the panel’s own y-axis label at 1027, $\mathbb{P}\{w > \overline{w}(\pi)\}$, says what the quantity is, so only the legend is wrong. In the other direction, the lowercase letters are used for the distributions throughout the prose: ‘\(f\) is \(\operatorname{Beta}(1, 1)\)’ and ‘\(g\) is \(\operatorname{Beta}(3, 1.2)\)’ (216-217, where \(f\) and \(g\) are densities and Beta(1,1) is a distribution), ‘\(f\) is a less attractive offer distribution than \(g\)’ (256), ‘if \(f\) generates successive wage offers’ and ‘if \(g\) generates wage offers’ (1096, 1099), ‘the worker believes the true distribution is \(g\)’ (1111) - while the Examples section switches back to the uppercase for the same objects (‘\(F\) ~ Beta(1, 1), \(G\) ~ Beta(3, 1.2)’ at 1066, 1123, 1140, 1152, 1178; ‘\(G\) has the same mean as \(F\)’ at 1125; ‘\(G\) is now an “inferior” distribution to \(F\)’ at 1128). After 129 the uppercase letters never appear again until line 872, so a reader has no way to know whether \(f\) and \(F\) are being distinguished on purpose.

  • [qe-writing-003] (reviewer) — Maintain logical flow. Count: 4. Lines: 38, 478, 820, 21. Example: 38 links the previous lecture in this series by raw URL - ‘previously studied’ - while line 84 links the same lecture correctly, ‘{doc}`in the baseline model <mccall_model>`’; mccall_model.md is in lecture-dp, so the raw URL both breaks the convention and points out of the book. Second, the two internal cross-references are written as markdown links to MyST targets rather than as references: ‘looking forward’ (477-478) and ‘shown above’ (693). Without a leading # these resolve as relative document paths, not as the (looking-forward)= and (take-1-solution-by-vfi)= labels defined at 241 and 268, so both are broken - and neither link rule reports an intra-document reference of this shape. Third, the Appendix A simulation initialises everyone as employed - e = np.ones((N, T+1)) at 820 - in a lecture whose entire subject is an unemployed worker deciding when to accept, with the only explanation being the garbled comment at 832 (‘# If agent is currently employment’); the reader has to infer that the simulation is of a steady-state labour market with separation rate s, which 771-780 never says. Fourth, the H1 at 21 numbers this lecture ‘Job Search VIII’ while the otherwise near-identical copy in lecture-python.myst numbers it ‘Job Search IX’ - one of the two series has it wrong.

  • [qe-writing-004] — Avoid unnecessary capitalization in narrative text. Count: 1. Lines: 705. Example: mid-sentence ‘Distribution’.

  • [qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 2. Lines: 1128, 283. Example: the three bold spans in the file are all correct - reservation wage (114) and reservation wage functional equation (548) are definitions, and Summary: (264) is a label - and italic is never misused, because italic is never used at all in 1185 lines. The gap shows where a term is introduced in a special sense and gets typographic quotes instead: ‘\(G\) is now an “inferior” distribution to \(F\)’ (1128) and ‘\(G\) is even more “inferior”’ (1143), and the model’s own name in scare quotes at 283 (‘a given parameterization of the “offer distribution unknown” model’) and 705 (‘This code solves the “Offer Distribution Unknown” model’), the second of which is also the qe-writing-004 hit. Italic is the convention for the first pair; the model name should either be bolded once where it is defined at 116 or set in plain text.

  • [qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 3. Lines: 981, 471, 1064. Example: job_search_example (951-1057) produces the lecture’s analytical centrepiece - a 2x2 panel of the likelihood ratio, the accept/reject regions with belief-update arrows, the two densities, and the acceptance probability under each distribution - and then a second 2-panel figure of empirical CDFs. It is called five times (1118, 1135, 1147, 1173, 1184), producing ten figures, none of which carries a :name: or a caption, so all the prose can do is point positionally: ‘the red arrows in the upper right figure’ (1068), ‘the lower right figure sheds light on which effect dominates’ (1103), ‘That graph shows that …’ (1109). Worse, 1158 asks for a cross-example comparison - ‘Comparing outcomes to the baseline case (example 1)’ - across roughly a hundred lines and eight intervening figures. Second, the ‘reject’ and ‘accept’ annotations are placed at hard-coded coordinates (471-472, 759-760, 996-997: ax.text(0.5, 0.6, 'reject'), ax.text(0.7, 0.9, 'accept')) while the shape and level of \(\bar w(\pi)\) change from example to example - 1131-1132 says that in Example 2 the curve is increasing rather than decreasing - so nothing guarantees the two labels still fall inside the regions they name in Examples 2 through 5. Third, the sequence of five examples varies two parameters (\(G\)’s Beta parameters and \(c\)) and the payoff is a comparison of \(\bar w(\pi)\) curves; one overlay figure of the five reservation-wage functions on shared axes would make the comparative statics visible in a way that ten separate 2x2 panels cannot.

Low severity#

  • [qe-fig-006] — Lowercase axis labels. Count: 1. Lines: 849. Example: axis label Time.

  • [qe-ref-001] — Use correct citation style. Count: 1. Lines: 79. Example: {cite} in narrative flow: ‘{cite}’.

Strengths#

  • The contraction argument is given in full and at the right level of detail: the function space and norm are declared (556-559), the operator \(Q\) is defined as a display (563-569), the equivalence between its fixed points and the RWFE is stated both ways (571-573), the bound is built in three steps with the elementary inequality \(|\max\{a,b\} - \max\{a,c\}| \leq |b-c|\) isolated as its own numbered equation (592-596), and the two consequences - existence of a unique \(\bar w\) and uniform convergence of \(Q^k\omega\) from any starting point - are drawn out separately (622-625).

  • The reduction from a two-dimensional value function to a one-dimensional reservation-wage function is motivated before it is derived: 486-497 says what will be gained (‘one dimensional rather than two dimensional’, ‘no maximization step’, ‘orders of magnitude faster than VFI’) and why it is worth the algebra (‘when it comes to programming, a bit of mathematical analysis goes a long way’), and then 501-544 derives {eq}`odu_mvf4` in four displays from the indifference condition at \(w = \bar w(\pi)\).

  • The lecture predicts the shape of the answer before computing it. 242-266 argues that the policy must be a threshold, that the threshold must depend on \(\pi\), and that it must be decreasing in \(\pi\) - with the two-step reason given (\(f\) is the less attractive distribution, larger \(\pi\) puts more weight on it) - and then 477-482 goes back and checks the computed policy against that prediction (‘The black line in the figure above corresponds to the function \(\bar w(\pi)\) introduced there. It is decreasing as expected.’).

  • Bayes’ rule is not asserted but shown: {eq}`odu_pi_rec_2` at 157-162 is followed immediately by the two displays it comes from (166-176), and the two objects the recursive formulation needs - the mixture density \(q_\pi\) and the update map \(\kappa\) - are defined as displays (183-191) and then appear under the same names in the code (334-341, 646-653).

  • The two solution methods are implemented against the same SearchProblem instance with the same Monte Carlo draws (sp.w_f, sp.w_g at 311-312), so the exercise at 686-697 - ‘Your result should coincide closely with the figure for the optimal policy shown above’ - is a genuine cross-check of two independent algorithms rather than a restatement.

  • Appendix B builds the one figure that answers the question the model is for: 1090-1112 poses the two countervailing effects (under \(f\) offers are lower but \(\pi\) rises and the reservation wage falls; under \(g\) offers are higher but \(\pi\) falls and the reservation wage rises), says that the question is which dominates, and then the lower-right panel measures exactly that, with the empirical CDFs of unemployment duration (1043-1048) as the confirmation.

  • The five examples move one thing at a time and each states its prediction first: same mean and smaller variance makes \(G\) inferior so \(\bar w\) turns increasing (1123-1132), smaller variance still makes the slope steeper (1140-1144), higher \(c\) buys a longer learning period so acceptance comes later and \(\pi\) is closer to 0 or 1 at acceptance (1152-1170), lower \(c\) the reverse (1178-1181).