two_computation#

  • Series: lecture-python.myst

  • File: lectures/two_computation.md

  • Audit date: 2026-08-26

  • Corpus snapshot: e25fdf2345

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

  • Overall score: 6.2 / 10

  • Priority: HIGH

Score breakdown#

Category

Score

One-line note

Writing

5.5/10

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

Math

3/10

qe-math-002 ×15; qe-math-010 (proposed) ×6; qe-math-004 ×2, +1 more.

Code

6/10

qe-code-002 ×9; qe-code-001 ×7.

JAX

out of scope

JAX rules target lecture-jax.

Figures

3/10

qe-fig-003 ×28; qe-fig-006 ×46; qe-fig-005 ×5, +3 more.

References

10/10

no mechanical violations detected.

Links

10/10

no mechanical violations detected.

Admonitions

N/A

no admonitions, exercises or solutions.

Issues#

Critical#

None found.

High severity#

  • [qe-code-001] (reviewer) — Follow PEP8 unless closer to mathematical notation. Count: 7. Lines: 1134, 1986, 2222, 2240, 2252, 2277, 2776. Example: 1982-1994 defines buyout_compensation_exp1_exo, which is never called anywhere in the lecture and would raise if it were: line 1986 is policy_seq[S1 + 1:S2 + 1, 0] = τ_l_trans applied to policy_seq_base.copy(), and policy_seq_base comes from make_policy_seq (1116-1128), which builds a jnp array - JAX arrays are immutable and reject in-place item assignment. The prose at 1977 introduces it as though it were the working step (“The function buyout_compensation_exp1_exo computes the present-value compensation for each cohort alive at the reform date and adds it to their initial assets”), while 1997-2007 actually does the job through find_transition_exo. Beyond that: 1134, 1270 and 1272 are f-strings with no placeholders (F541); 2240-2241 selects a default by truthiness, RR = RR_init if RR_init else tech.RR, where is None is meant; 2252 and 2362 wrap pure-JAX computations in except ValueError, which those calls cannot raise, and 2363 then substitutes a hard-coded τ_l_trans = 0.35; neither relaxation loop (2245-2277, 2355-2391) says anything when max_iter is exhausted, so a non-converged steady state or price path is returned as though converged; 2776 rebinds n as a length inside _agg_c where n is the population growth rate everywhere else in the lecture; exponentiation spacing is inconsistent (β_t**2 at 426, π**2 at 673 and 681 against k_per_eff ** - 1) at 2222 and 2319); the continuation lines at 876-877, 974-976, 992-994 and 1218-1223 sit one space past the opening delimiter (E128); and 90 lines carry trailing whitespace, including runs of spaces on otherwise blank lines inside code cells (657, 661, 665, 669, 683, 1030, 1310, 1318, 1345, 1352, 1388).

  • [qe-code-002] — Use Unicode symbols for Greek letters in code. Count: 9. Lines: 974, 992, 998, 1036, 1193, 1223, 1233, 1740, 1817. Example: spelled-out tau.

  • [qe-fig-001] — Do not set figure size unless necessary. Count: 10. Lines: 294, 599, 1578, 1885, 2037, 2096, 2152, 2509, 2727, 2923. Example: figsize=.

  • [qe-fig-003] — No matplotlib embedded titles. Count: 28. Lines: 602, 606, 711, 1895, 1907, 2046, 2064, 2104, 2114, 2123, …. Example: .set_title.

  • [qe-fig-005] — Descriptive figure names for cross-referencing. Count: 5. Lines: 2024, 2080, 2151, 2488, 2710. Example: code-cell figure without mystnb figure metadata.

  • [qe-fig-006] — Lowercase axis labels. Count: 46. Lines: 301, 302, 603, 607, 709, 710, 1167, 1586, 1587, 1893, …. Example: axis label Date born.

  • [qe-math-002] — Use \top for transpose notation. Count: 15. Lines: 153, 162, 173, 367, 368, 369, 377, 479, 488. Example: apostrophe transpose )'.

  • [qe-math-010 (proposed)] — Blackboard \mathbb{P}, \mathbb{E}, \mathbb{V} with braces. Count: 6. Lines: 149, 153, 227, 358. Example: bare expectation E_t[.

  • [qe-writing-008] — Remove excessive whitespace between words. Count: 7. Lines: 51, 91, 133, 229, 341, 380. Example: 2 spaces.

Medium severity#

  • [qe-fig-004] — Caption formatting conventions. Count: 2. Lines: 1143, 1571. Example: caption of 10 words.

  • [qe-fig-008] — Use lw=2 for line charts. Count: 4. Lines: 601, 605, 708, 1160. Example: plot() without lw=.

  • [qe-math-004] — Do not use bold face for matrices or vectors. Count: 2. Lines: 459, 472. Example: \mathbf.

  • [qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 3. Lines: 97, 224, 367. Example: 97 announces three interchangeable spellings for every variable and the lecture then uses all three: “we use subscript \(t\) to denote age, argument \(s\) in parentheses to denote calendar time, and superscript \(s-t\) to denote date of birth, so that \(z^{s-t}(s) \equiv z_t(s) \equiv z^{s-t}_t(s)\)”. Having said the three are the same object, the resource constraint at 180 and the government budget constraint at 241-243 write \(N^{s-t}_t\) while 105-124 writes \(N_t(s)\) and \(N_0(s)\) - so the reader must carry a translation table through the two longest displays in the lecture, for no gain, when one spelling would do. Second, one primitive has three names: the preference shock is \(\gamma_t\) at 224, \(\gamma\) at 474, and \(\bar{\gamma}\) in the calibration table at 529 and as γ_bar in the code at 643. Third, the tilde is carrying meaning the letters should carry: \(\tilde\alpha\) at 193-197 is capital’s share while \(\alpha_t\) at 103 is a survival probability, and \(\tilde A\) at 193 is total factor productivity while \(A_t\) at 146 and 361 is the state transition matrix - two pairs of unrelated objects separated only by a diacritic, which is why the code has to spell them α_tilde and A in one namespace (720-728).

  • [qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 4. Lines: 183, 219, 2192, 2578. Example: 183 is a single 75-word where-clause that defines seven symbols in a row (\(g(s)\), \(K(s-1)\), \(R(s-1)\), \(\varepsilon_t\), \(w(s)\), \(N_0(s)k_{-1}(s)\), \(\delta\)), and 219 does the same in 60 words while also embedding the entire tax rule, \(T_t(s) = \tau_0(s) + \tau_{\ell}(s)(w(s)\varepsilon_t + d_t) + \tau_a(s)(R(s-1)-1)a_{t-1}(s-1)\), inside the gloss - and \(\tau_\ell\) is the instrument the whole lecture solves for. The file already uses bullet lists for exactly this job at 315-318, 510-512 and 261-265, so the tool is at hand. Two passages are also repeated verbatim: 2192 restates 330 word for word (“changes in saving behavior affect capital accumulation, which alters marginal products and feeds back into household decisions”) and 2578 restates 331 (“lower interest rates benefit young workers through higher wages, but hurt retirees through lower returns on savings”).

  • [qe-writing-003] (reviewer) — Maintain logical flow. Count: 4. Lines: 516, 1141, 2853, 2951. Example: 2951 and 2973 announce “the asset variance surfaces” and “the consumption variance surfaces”, but the cells at 2953-2959 and 2975-2981 pass transform=np.sqrt with zlabels 'Std Dev Assets' and 'Std Dev Consumption' - the prose names one quantity and the axes name its square root, four times over. 2853 says “The 3D surface plots below show how assets and consumption evolve”, and the cell immediately below (2855-2937) produces no output at all: it defines compute_variances and plot_surface_grid across 82 lines, and the first surface does not appear until 2944. 1141 introduces the figure as tracing “how the equilibrium labor tax rate varies with government debt” while its own caption at 1147 reads “Debt-to-GDP ratio as a function of the labor tax rate” - the two readings are inverses, and the loop at 1154 varies \(\bar b\) and records \(\tau_\ell\), so the caption has the causality backwards. And 516 says “In all exercises, we set \(T_0 = 65\), \(T_1 = 43\), \(s_1 = 0\), and \(s_2 = 40\)” in a lecture with no exercises. Underlying all of this is symbol overloading the prose never flags: \(S\) is the social security benefit at 216-219 and 1932-1952 and the transition horizon at 1091-1097 (“horizons \(S = 140\)” appears two lines after a paragraph about benefits), and \(R\) is the gross return at 183 and the LQ state cost matrix at 358 and 479 - a collision visible in solve_riccati_step’s own signature at 389, where R is the cost matrix and RR is the return.

  • [qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 3. Lines: 189, 742, 758. Example: the emphasis markers are mostly used as structure rather than for definitions and emphasis. 189-190 italicise the two named price regimes, Small open economy: and Closed economy:, which are definitions the lecture then refers back to as headings (311, 324) and in prose throughout - bold is what the rule assigns to those. Thirteen further sites use a marker as a pseudo-heading: bold at 742, 747 and 752 inside the {prf:algorithm} list, and italic at 758, 804, 838 (Phase 1.-Phase 3.), 1930, 1932, 1937, 1945 and 2201 (Step 1.-Step 4.). No italic in the file is doing emphasis, and no bold outside 261-273 is doing definition.

  • [qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 2. Lines: 329, 2851. Example: the hardest thing in this lecture is the nesting of its loops, and it is never drawn. 329 says “The computation requires nested iteration: an inner loop determines labor income tax rates, and an outer loop adjusts interest rates to clear factor markets”, and 2203-2211 spells the same structure out as indented pseudo-text inside a {prf:algorithm}; the real depth is four levels (price relaxation at 2355-2391, bisection on \(\tau_\ell\) inside find_transition_exo, the backward Riccati scan at 1319-1351, the forward moment scan at 1400-1425), and a reader has to reconstruct it from fourteen function introductions. The lecture has one diagram, the age-time chart at 287-309, and it is the clearest thing in the file - which is the argument for a second. Second, the sixteen 3D surfaces at 2944-2982 are drawn with edgecolor='none', alpha=0.8 and no colorbar (2929), so the reader is asked to read four-way comparisons off shaded surfaces with nothing to anchor a value to; the claims made about them (2941 “peak assets shifting”, 2951 “dispersion to increase with age”) are one-dimensional statements that slices at fixed \(s\) would show far better than a perspective view.

Low severity#

None found.

Strengths#

  • The age-time diagram at 287-309 is drawn before it is needed and explained before it is drawn: 283-285 says that each horizontal line is one cohort’s lifetime and the vertical dashed lines are \(s_1\) and \(s_2\), and 322 then reads the aggregation rule off it (“summing across all living cohorts (along a vertical line in the age-time diagram)”) - which is exactly how the transition code aggregates at 1493 and 1502.

  • 261-273 defines all five equilibrium objects in bold, in dependency order - allocation, government policy, price system, equilibrium, stationary equilibrium - with the two equilibrium conditions numbered at 269-271 and the stationary case’s simplified budget constraint written out at 276.

  • The three {prf:algorithm} blocks are labelled and actually cross-referenced instead of repeated: two_comp_algo_buyout (1925), two_comp_algo_endo (2194) and two_comp_algo_govfund (2590), with 2199 and 2595 both opening “Steps 1–3 are the same as {prf:ref}`two_comp_algo_buyout`” and then naming only what differs.

  • The shooting method is shown before it is automated: 1553-1567 solves the transition at \(\tau_\ell = 0.14\) and \(0.17\), 1571-1589 plots both debt paths against the target with the \([s_1, s_2)\) window shaded, and 1592-1596 reads off which overshoots - so bisection arrives as the obvious next step rather than as an assertion.

  • 1150-1175 computes the debt/tax locus on a 20-point grid, marks the initial steady state on it, and then states the inversion problem the terminal steady state requires (“given a target debt-to-GDP ratio, find the debt level \(\bar b\) and the associated \(\tau_\ell\)”) - the root-find is motivated by a curve the reader has already seen.

  • The buy-out is validated rather than described: 2035 recovers the compensation as the difference in initial assets between the two solves, 2051-2059 splits it into workers and retirees as separate bar series, and 2072-2076 reads off the three things the actuarial formula at 1948 predicts - retirees get the most, the profile declines with age among retirees, young workers get almost nothing.

  • Every one of the fourteen computational steps is introduced by one sentence naming its contract before its cell appears: solve_riccati_step (380-382), forward_moment_step (491), the three _ss_* phases (758, 804, 838), ss_imbalance (872), _grid_refine (933), _find_ss_tau_l (971), _compute_gdp (1007), find_ss_exo (1021), solve_backwards (1283), simulate_forwards (1375), find_ss_endo (2228) and find_transition_endo (2333) - and the SteadyState namedtuple at 910-928 carries an inline comment on each of its seventeen fields.

  • The four-scenario comparison at 2710-2849 is set up once and applied six times: one label list and one linestyle list (2730-2732) drive three time-series panels and three bar charts, so buy-out against government-funding and fixed against endogenous prices are legible in a single figure, with the value printed on each bar (2809-2835).

  • 1091 states the two horizons and why they differ - \(S = 140\) under exogenous prices, \(S = 200\) under endogenous - which follows 328’s reason for truncating at \(s_2 + 2T_0\) when prices keep moving after policy stabilises.