survival_recursive_preferences#

  • Series: lecture-python.myst

  • File: lectures/survival_recursive_preferences.md

  • Audit date: 2026-08-26

  • Corpus snapshot: e25fdf2345

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

  • Overall score: 8.2 / 10

  • Priority: LOW

Score breakdown#

Category

Score

One-line note

Writing

5/10

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

Math

9.5/10

qe-math-009 ×3.

Code

8.5/10

qe-code-001 ×3.

JAX

out of scope

JAX rules target lecture-jax.

Figures

4.5/10

qe-fig-003 ×6; qe-fig-004 ×5; qe-fig-005 ×1, +1 more.

References

10/10

no mechanical violations detected.

Links

10/10

no mechanical violations detected.

Admonitions

10/10

no mechanical violations detected.

Issues#

Critical#

None found.

High severity#

  • [qe-fig-001] — Do not set figure size unless necessary. Count: 8. Lines: 687, 817, 848, 914, 990, 1076, 1115, 1201. Example: figsize=.

  • [qe-fig-003] — No matplotlib embedded titles. Count: 6. Lines: 762, 869, 1127, 1137, 1207, 1219. Example: .set_title.

  • [qe-fig-004] — Caption formatting conventions. Count: 5. Lines: 667, 1065, 1108, 1160. Example: caption of 7 words.

Medium severity#

  • [qe-code-001] (reviewer) — Follow PEP8 unless closer to mathematical notation. Count: 3. Lines: 618, 712, 965. Example: 47 lines of the lecture’s code are dead. compute_survival_boundary (618-649) and compute_limit_boundary (652-664) are called at 693-694 and 701-704, their results bound to ρ_1 and ρ_2, and neither name is ever read again: the panels draw their boundary curves with ax.contour(G, R, m0, levels=[0]) and ax.contour(G, R, m1, levels=[0]) at 723-726 instead. The omission is easy to miss because the dead algebra is correct - solving pr + (1-ρ)/ρ · subj_ret = 0 really does give ρ = subj_ret/(subj_ret - pr), which is what 647 returns. Two more bindings are computed and discarded in the same cell: ag2_dom (712) and either (713) are never used, and ag1_dom (711) only decides whether to draw a text label. And simulate_crra_pareto (965-983) and simulate_pareto_share_toy (1167-1188) are one routine written twice - same default_rng, same n_steps = int(T/dt), same t_grid, same pre-drawn dW, same Euler loop, same logistic transform - differing only in whether the drift is a constant or a function of the current share.

  • [qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 3. Lines: 193, 403, 976. Example: the two most-used symbols in the lecture are variant Greek letters that most fonts render almost identically to their plain counterparts: \(\upsilon\) for the Pareto share (193, and roughly 60 further uses) reads as \(v\), and \(\vartheta\) for its log-odds (198) reads as \(\theta\) - and \(\theta\) is otherwise unused, so the resemblance is not even harmless. The code gives the game away by naming the log-odds array θ at 976 and 1179. Second, \(y^n\) is the wealth-consumption ratio (403: “\(c^n = C^n / A^n = (y^n)^{-1}\)”, used at 385, 507, 526-528, 1025, 1047) while \(Y_t\) is the aggregate endowment (84) - two unrelated objects separated only by case - and the code then writes the endowment volatility as σ_y in lower case (573, 675, 810), so the code’s y denotes the math’s \(Y\) and the math’s \(y\) never appears in code at all. Third, four decorated symbols coexist for four different jobs (\(\tilde J\), \(\tilde V^n\), \(\tilde\lambda^n\), \(\bar\mu_R\)), and the superscript indices \(\omega^1, \omega^2\) become subscripts ω_1, ω_2 in every function signature.

  • [qe-writing-001] — Use one sentence per paragraph. Count: 1. Lines: 158. Example: 2 sentences in one paragraph.

  • [qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 3. Lines: 434, 936, 782. Example: 434 repairs a displayed equation from inside the solution to an exercise: “The volatility terms \(\tfrac{1}{2}[(\sigma_R^1)^2 - (\sigma_R^2)^2]\) are absorbed into \(m_R^1 - m_R^2\) when we define \(m_R^n\) as the expected log portfolio return (i.e., the drift of \(\log R^n\) rather than the arithmetic return), giving {eq}`eq:wealth_decomp`” - 38 words that redefine a symbol introduced at 378 in order to make an equation displayed at 382 come out right. The definition belongs at 378, where the symbol is introduced. 936 is 40 words with the outcome, its mechanism and its probabilistic qualifier all stacked after one colon (“neither boundary is repelling, so whichever agent happens to get ahead early will dominate, with each agent having strictly positive probability of dominance depending on the realized Brownian path”). And 782 is 38 words whose subject changes at the comma.

  • [qe-writing-003] (reviewer) — Maintain logical flow. Count: 4. Lines: 593, 938, 289, 117. Example: boundary_drift (593-602) multiplies the two channels by \(\gamma\) - return γ * (portfolio_return_diff(...) + saving_channel(...)) - and no equation in the lecture contains that factor. {eq}`eq:wealth_decomp` (382), {eq}`eq:portfolio_returns` (443) and {eq}`eq:consumption_rates` (506) all give the bracketed sum itself, so the step \(m_\vartheta = \gamma\,(m_A^1 - m_A^2)\) that the code relies on is nowhere stated. I confirmed the factor numerically: boundary_drift / decompose_survival(...)[3] is exactly \(\gamma\) at \(\gamma = 0.5, 1, 2, 5, 10\). The visible cost is that two figures label their vertical axis “boundary drift” while plotting quantities that differ by a factor running from 0.5 to 25 across the plotted range - 914-919 uses boundary_drift, 1115-1129 uses the total from decompose_survival - so the levels in {numref}`fig-boundary-drifts-small-gamma` and {numref}`fig-optimistic-pessimistic-drifts` are not comparable. Second, 932-940 describes two regimes where its own figure shows three. With that cell’s parameters (\(\omega^1 = 0.25\), \(\omega^2 = 0\), \(\rho = 0.67\), \(\sigma_Y = 0.02\)) the \(\upsilon \to 1\) drift crosses zero at \(\gamma \approx 0.38\) and the \(\upsilon \to 0\) drift at \(\gamma \approx 1.46\), so on \(\gamma \in (0.38, 1.46)\) - a quarter of the plotted range - both drifts are negative, which is (i’) and (ii), outcome ©, agent 2 dominates. The prose goes straight from outcome (d) at small \(\gamma\) (936) to outcome (a) (938-940, “As \(\gamma\) increases past roughly 1, the blue curve crosses zero and becomes positive while the red curve stays negative”) and never mentions the regime in between. Third, the exact reduced ODE is split across four separate $$ blocks (289-312), so only the first fragment carries the label eq:hjb and the other three render as three further unlabelled numbered displays - which is why 316-322 has to narrate “The first line … The second line … The third line … The fourth line” of what the reader sees as four equations; and the ODE is then never solved, as 1156 and 1236 concede. Fourth, {eq}`eq:felicity` (117-122) is undefined at \(\gamma = \rho\) - \(\rho - \gamma\) sits in a denominator and \((\gamma-\rho)/(1-\rho)\) in an exponent - while the note at 133 says “When \(\gamma = \rho\), preferences reduce to the standard separable CRRA case” and a whole section (942-1007) works in that case. The reduction is a limit, and the lecture never says so.

  • [qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 3. Lines: 48, 453, 36. Example: the lecture contains exactly two bold spans in 1248 lines - market selection hypothesis (36) and infinitesimal generator (265) - and italicises the definitions that carry the argument. The three channels the entire lecture is organised around are coined in italic at 48-50 (risk premium channel, speculative volatility channel, saving channel) and then referred to in plain text at 54, 517, 519, 790 and 839; volatility penalty is coined in italic at 453 and referred to plainly at 835, 1149. Those four are definitions in exactly the sense that the two bolded terms are. Meanwhile the lecture’s genuine word-level emphasis - subjective at 511 and 530 - is also italic, so italic carries both jobs and bold carries almost none.

  • [qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 2. Lines: 736, 351. Example: the survival-regions figure computes a full classification of the \((\gamma,\rho)\) plane and then labels it by hand. 710-713 builds four boolean masks - both, ag1_dom, ag2_dom, either - covering every grid point, and 736-760 places the region names as ax.text calls at hard-coded coordinates chosen per panel (ax.text(1.8, 1.55, "agent 2\\ndominates") and eleven more), with two of the four masks never used at all. The labels therefore carry no guarantee of sitting inside the regions they name, and any change to \(\sigma_Y\), \(\omega^1\) or the axis limits silently invalidates twelve hard-coded positions; a contourf over the four masks with a legend would take the labelling from the computation that is already being done. Second, {prf:ref}`survival_conditions` sets up a four-way taxonomy (351-357) that the summary at 1240-1248 leans on, and no figure shows what the four outcomes look like as paths. simulate_pareto_share_toy (1167) already produces Pareto-share paths from any \((\gamma, \rho, \omega^1, \omega^2)\); four small panels - both survive, agent 1 dominates, agent 2 dominates, either dominates - would complete a taxonomy of which the lecture currently illustrates one case (1201-1225) and its separable opposite (958-1000).

Low severity#

  • [qe-fig-005] — Descriptive figure names for cross-referencing. Count: 1. Lines: 794. Example: code-cell figure without mystnb figure metadata.

Strengths#

  • Five of the lecture’s central derivations are handed to the reader as exercises with hints and worked solutions (153-186, 208-243, 394-437, 457-500, 521-563), and the solutions do real work rather than restating the result: 220-224 establishes \(\vartheta = \log\tilde\lambda^1 - \log\tilde\lambda^2\) before subtracting the two SDEs, and 489 supplies the factorisation \((\pi^1)^2 - (\pi^2)^2 = (\pi^1-\pi^2)(\pi^1+\pi^2)\) that makes {eq}`eq:portfolio_returns` collapse to the form displayed.

  • The discrete-to-continuous bridge at 251-283 is the best exposition in the lecture: it writes the discrete Bellman equation the reader already knows (258), shrinks the period, introduces the generator with Ito’s lemma (267-271), and then says outright what the new object corresponds to - “This is the continuous-time analogue of \(\beta\,\mathbb{E}[\tilde J(\upsilon')] - \tilde J(\upsilon)\)” (275) - before the exact HJB arrives.

  • The saving channel’s sign is derived rather than asserted: 513-517 gives the substitution-versus-income argument for each of IES \(> 1\), IES \(< 1\) and IES \(= 1\), and the three-panel figure at 841-875 then shows the three signs side by side at the same \(\omega^1\) and \(\sigma_Y\).

  • 881-883 pre-empts the mistake a reader of that figure would make: the IES \(= 1\) panel “eliminates the saving channel but does not by itself reproduce the full separable CRRA benchmark, which requires \(\gamma = \rho\) (i.e., IES \(= 1/\gamma\)), not merely \(\rho = 1\)”.

  • The toy simulation is labelled a toy three times and its limits are stated exactly: 1156 (“only a toy approximation”), 1158 (“it illustrates the recurrence logic without solving the full equilibrium ODE”) and 1236, which names the specific figure of the source paper it does not reproduce and what would be needed to reproduce it.

  • The separable benchmark is simulated rather than only described (958-1007), and 1230 then compares the recursive-preference paths against it by {numref}`fig-crra-pareto-paths` - so the two figures are explicitly the same experiment under two preference specifications, and the difference the lecture is about is visible as a difference between two pictures.

  • 568-570 states two invariance results before any figure is drawn - survival depends on the signal-to-noise ratios \(\omega^n/\sigma_Y\) rather than on \(\omega^n\) and \(\sigma_Y\) separately, and not at all on \(\beta\) or \(\mu_Y\) - which is exactly what a reader needs in order to know that the eight figures that follow are entitled to hold those parameters fixed.

  • The arithmetic checks out where I tested it: decompose_survival (795-804) reproduces the first term of {eq}`eq:portfolio_returns` exactly (\(\Delta\omega(\gamma\sigma_Y - \omega^2)/\gamma\) equals \(\frac{\Delta\omega}{\gamma\sigma_Y}(\gamma\sigma_Y^2 - \omega^2\sigma_Y)\) at every \(\gamma\) I tried), the volatility and saving terms match {eq}`eq:portfolio_returns` and {eq}`eq:consumption_rates` as written, and IES \(= 1/0.67 = 1.49\) as 830, 925 and 1145 all say.