odu#
Series: lecture-python.myst
File:
lectures/odu.mdAudit date: 2026-08-26
Corpus snapshot:
e25fdf2345Categories 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 |
|
Math |
9/10 |
|
Code |
7.5/10 |
|
JAX |
out of scope |
JAX rules target |
Figures |
5/10 |
|
References |
9/10 |
|
Links |
8.5/10 |
|
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, 645, 727, 799, 970, 1009. Example: 371 opens
get_greedy’s docstring with four quotes,"""", so the docstring begins with a stray"; 970-972 assign two lambdas to names (l = lambda w: f(w) / g(w),obj = lambda w: l(w) - 1.), which PEP8 asks to bedefs, and the first usesl, the identifier PEP8 singles out as ambiguous - as do 230-231 and 793; 799 writesif e == False:whereif not e:is asked for, andethere is a float read from an array of ones, so the comparison works only because0.0 == False; 1009 assigns the quiver artist toq, never uses it, and shadows the lecture’s own symbol for the unknown wage density (94, 123, 131, 184); 645-653 repeatsκverbatim from 333-341 and 635-639 repeats the six unpacking lines of 323-327, whileget_greedy(369-394) is a copy ofT(343-367) withmax(v_1, v_2)swapped forv_1 > v_2; 727 leaves two unused names; and 351-359, 377-385 and 665-669 nestprangeinsideprange, which numba only parallelises at the outermost level, so the inner two read as parallel and are not.[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: 129, 149, 399, 1109, 1159. Example: several typos land on load-bearing sentences: “repectively” at 129, in the line that establishes the \(F\)/\(f\) notation; “about the the distribution of \(W_t\)” at 149; “unemployment compensation if low” at 1159; and “the priori is strong” at 1088 (repeated as a code comment at 906) where the word is prior, the lecture’s own central object. Line 399-400 presents 77 lines of code (320-397) and then declines to explain any of it - “We will omit a detailed discussion of the code because there is a more efficient solution method that we will use later” - which leaves the reader carrying
operator_factory,v_func,κ,Tandget_greedywith no commentary. Lines 1109-1112 are a 46-word sentence stacking the claim, the qualifier, the boundary case and the reason for the boundary case. And 496-497 ends the “Take 2” section on a general aphorism (“when it comes to programming, a bit of mathematical analysis goes a long way”) rather than on what the method is.[qe-writing-003] (reviewer) — Maintain logical flow. Count: 5. Lines: 484, 693, 699, 727, 1015. Example: the heading tree does not match the argument. “## Take 1: Solution by VFI” at 269 is an H2 that contains the whole first method, but “## Take 2: A More Efficient Method” at 484 contains only four paragraphs of promise (486-497) - the actual method lives in three sibling H2s that follow (“Another Functional Equation” 499, “Solving the RWFE” 551, “Implementation” 627), so “Take 2” is an empty section and the contrast the lecture is built on is invisible in the table of contents. Worse, the machinery the rest of the lecture depends on is defined inside a collapsed solution:
solve_wbaris written at 715-745 inside{solution-start} odu_ex1 :class: dropdown, and it is then called by Appendix A at 789 and byjob_search_exampleat 967 - so Examples 1 to 5 will not run unless the reader has expanded exercise 1’s solution. Line 699 puts a## Solutionsheading over that single solution. Line 693 sends the reader to “the figure for the optimal policy shown above”, but that anchor (268) points at the start of the section, not at the policy figure 190 lines later at 462.solve_wbaralso carriesm, n = len(sp.w_grid), len(sp.π_grid)at 727, dead code copied fromsolve_modelwhere it was used at 427. And 1015-1016 plotsf(x_grid)andg(x_grid)using thex_gridbound at line 229 inside the first figure cell, 786 lines earlier, sojob_search_examplesilently depends on a module global that nothing in its signature mentions.[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-001] — Use markdown style links for lectures in same lecture series. Count: 4. Lines: 38, 41, 861, 1071. Example: full URL to own series (python.quantecon.org).
[qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 4. Lines: 265, 558, 590, 1009. Example: line 265 writes the conjectured policy as
$\mathbb 1{w\geq \bar w(\pi) }$with unescaped braces, so the braces are read as TeX grouping and the rendered output is \(\mathbb 1 w \geq \bar w(\pi)\) with the indicator’s argument no longer delimited - and it is the same object that 113 writes as\mathbf{1}\{w \geq \bar w\}, so the lecture has two spellings, one of which does not render. The reservation wage is \(\bar w\) in the model (114, 251, 266, 481, 509, 541) and \(\overline{w}\) in the examples (1027, 1132, 1144, 1166). Line 558 introduces \(b[0,1]\) for the space of bounded functions and then 590-595 states{eq}`odu_nt2`“for real numbers \(a, b, c\)”, where \(b\) is that function space and \(c\) is the unemployment compensation used in the display two lines above at 567 - three meanings for \(b\) and \(c\) inside forty lines. And \(\omega\) is a generic element of \(b[0,1]\) throughout 558-625 while 175 already used \(\omega\) as an index ranging over \(\{f, g\}\).[qe-math-015 (proposed)] (reviewer) — Lowercase for densities/PMFs, uppercase for CDFs. Count: 3. Lines: 131, 1022, 1066. Example: 127-129 states the convention exactly right - “two possible distributions \(F\) and \(G\)” whose “densities” are \(f\) and \(g\) - and then the lecture abandons it in both directions. From 131 onward the densities carry the distribution role: “nature selects \(q\) to be either \(f\) or \(g\)” (131), “the two possible distributions \(f\) and \(g\)” (137), “prior probability \(\pi_0\) on \(f\) having been chosen” (139), \(\mathbb{P}\{q = f\}\) (167-169). Then in the Examples section the uppercase letters come back for the same objects - “\(F\) ~ Beta(1, 1), \(G\) ~ Beta(3, 1.2)” (1066, 1123, 1140, 1152, 1178), “\(G\) has the same mean as \(F\)” (1125), “\(G\) is now an ‘inferior’ distribution to \(F\)” (1128) - so the reader meets two letters for one object 900 lines apart. And 1022-1023 label curves computed from
1 - beta.cdf(w_bar, F_a, F_b), i.e. from the CDFs, with the lowercase density letters'$f$'and'$g$', which is the exact inversion the rule is about.[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: 264, 1128. Example: the two genuine definitions are correctly bold (reservation wage 114, reservation wage functional equation 548), but the lecture uses no italic at all, so where it wants emphasis it reaches for something else: 264 opens a paragraph with the bold label
**Summary:**, and 1128 and 1143 put emphasis in scare quotes - “\(G\) is now an “inferior” distribution to \(F\)” - which the rule asks to be italic.[qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 4. Lines: 157, 551, 981, 1068. Example: the model section (116-266) derives a two-state dynamic program with Bayesian learning and has exactly one figure - the two densities at 222. The Bayes update
{eq}`odu_pi_rec_2`at 157-162 is the lecture’s central mechanism and its picture, the red arrow field showing how \(\pi_t\) moves in response to \(w_t\), exists at 1009 but is not shown until line 1118, some 950 lines later. The contraction argument at 551-625 is six displayed inequalities culminating in “\(Q^k \omega \to \bar w\) uniformly” (625) and nothing plots the iterates, thoughsolve_wbarcomputes them. The four-panel figure built at 981-1029 has no caption of any kind, so the prose at 1068-1115 has to identify panels by position - “the red arrows in the upper right figure”, “the lower right figure”, “That graph” - and the two CDF panels at 1041-1057 carry their titles inside the image viatitle.set_text. The lecture also has no{note}or{warning}anywhere, though 305 (# Avoids instabilityon the \(\pi\) grid bounds) and 399-400 (“We will omit a detailed discussion of the code”) are both notes written as code comments or asides.
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 lecture earns its second method rather than announcing it: 501-544 starts from the indifference condition at \(w = \bar w(\pi)\), combines
{eq}`odu_mvf`and{eq}`odu_mvf2`into{eq}`odu_mvf3`, and arrives at the one-dimensional functional equation{eq}`odu_mvf4`- so the claim at 491-494 that the new operator is one-dimensional with no maximisation step is derived, not asserted.The contraction proof is complete and elementary: 575-617 bounds \(|(Q\omega)(\pi) - (Q\omega')(\pi)|\) by the triangle inequality, supplies the auxiliary fact \(|\max\{a,b\} - \max\{a,c\}| \leq |b-c|\) as its own numbered display
{eq}`odu_nt2`, and takes the supremum - then states both consequences the algorithm needs (existence and uniform convergence from any starting point) at 624-625.The economics of the policy is predicted before it is computed: 244-266 argues that \(\bar w\) must be decreasing in \(\pi\) from two stated premises, labels the conjecture, and 477-482 then checks it against the computed contour plot - so the figure at 462 is a test rather than an illustration.
Probability notation follows the events-in-braces convention exactly where it matters (qe-math-014 (proposed)): every one of the six probabilities in the Bayes derivation at 167-175 is written \(\mathbb{P}\{\cdot\}\) with braces around a logical condition, and the axis label at 1027 keeps it,
$\mathbb{P}\{w > \overline{w}(\pi)\}$.Appendix A answers a question the model implies but never poses: 771-780 changes the true offer distribution mid-simulation and shows the unemployment rate spiking because agents are slow to learn, with the change date marked on the figure by
axvlineat 848 - a genuine comparative dynamic rather than another comparative static.Appendix B’s
job_search_example(951-1057) is a real instrument: one call produces the likelihood ratio \(l(w)\) with its two roots, the reservation-wage frontier with the belief-update arrow field, the two densities on the same axes, and the acceptance probability under each distribution - and Examples 1 to 5 (1064-1185) then change one parameter at a time and read the same four panels each time.Example 4 versus Example 1 is the sharpest comparison in the lecture: 1152-1170 raises only \(c\) from 0.3 to 0.8 and predicts three consequences - later acceptance, and a belief \(\pi\) closer to 0 or 1 at the moment of acceptance, hence better-informed acceptance - all three readable off the CDF panels the wrapper draws.
The
SearchProblemconstructor draws its Monte Carlo shock arrays once at construction (311-312) andoperator_factorycloses over them (324), soTis a deterministic map and the tolerance loop at 429-435 converges rather than chattering on fresh draws.
Recommended actions#
Move
solve_wbar(715-745) out of the exercise 1 solution into the## Implementationsection next toQ_factory: Appendix A (789) andjob_search_example(967) both call it, so five of the lecture’s sections currently depend on code hidden behind:class: dropdown.Fix the heading tree so “Take 2” contains its own method - demote “Another Functional Equation” (499), “Solving the RWFE” (551) and “Implementation” (627) to
###under 484 - and point 693’s cross-reference at the policy figure (462) rather than at the section anchor.Repair
$\mathbb 1{w\geq \bar w(\pi) }$at 265, whose unescaped braces drop the indicator’s delimiters from the rendered maths, and make it match\mathbf{1}\{\cdot\}at 113 (or make both\mathbb{1}\{\cdot\}); settle \(\bar w\) versus \(\overline{w}\) across 114 and 1027.Restore the \(F\)/\(f\) convention the lecture states at 127-129: either the densities \(f, g\) or the distributions \(F, G\) should carry the “nature chose this one” role, not both (131-175 versus 1066-1178), and the two curves at 1022-1023 are computed from CDFs and should not be labelled
$f$and$g$.Give the four-panel figure at 981 and the two CDF panels at 1041 real captions with
name:fields so 1068-1115 can name them instead of saying “the upper right figure”, and move the three embedded titles (850, 1047, 1054) into that metadata; while there, lowercase the axis labelsTime(849),Prob(time)(1048) andProb(π)(1055).Add the picture the model section lacks: the arrow field already coded at 1002-1009 belongs next to
{eq}`odu_pi_rec_2`at 157-162, not 950 lines later, and a plot of the iterates \(Q^k\omega\) would evidence the convergence claim at 625.Fix the code items: the four-quote docstring at 371, the three lambda assignments and the ambiguous name
l(230-231, 793, 970-972),if e == Falseat 799, the unusedqat 1009 and unusedm, nat 727, the duplicatedκ/unpacking blocks (635-653 versus 323-341), the nestedpranges (351-359, 377-385, 665-669), and the sevenfigsizeoverrides plus nineplot()calls missinglw=2.Sweep the writing: sentence-case the seven flagged headings (70, 82, 116, 242, 484, 499, 1064), make the four full URLs
{doc}links (38, 41, 861, 1071), make 79’s citation{cite:t}, italicise the emphasis at 264, 1128 and 1143, and fix “repectively” (129), “the the” (149), “priori” (906, 1088), “if low” (1159), “accpet_t” (892) and “works” for workers (818), plus the 23 double spaces.