perm_income#
Series: lecture-dp
File:
lectures/perm_income.mdAudit date: 2026-08-26
Corpus snapshot:
c30490a2f4Categories audited: writing, math, code, figures, references, links, admonitions (JAX out of scope)
Overall score: 7.0 / 10
Priority: HIGH
Score breakdown#
Category |
Score |
One-line note |
|---|---|---|
Writing |
3/10 |
|
Math |
4/10 |
|
Code |
7.5/10 |
|
JAX |
out of scope |
JAX rules target |
Figures |
6/10 |
|
References |
8.5/10 |
|
Links |
10/10 |
no mechanical violations detected. |
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: 5. Lines: 810, 829, 516, 513, 498. Example: 810 redefines
time_path, a function already defined at 482 with a different signature and a different meaning - the first returnsw, b, cfor the IID-income example, the second returnsb, cfor the impulse-response example and takes apermanentflag - 328 lines apart with no note that the name is being reused, so a reader who re-executes the earlier cell silently gets the wrong function. 829 bindsL = 0.175as a plot limit in a lecture that has just told the reader ‘\(L\) is the lag operator’ (705) and written \(d(L) = \sum_j d_j L^j\) (705, 1001). 516 pulls in the stdlibrandommodule (imported at 52) solely to randomise line colours,rcolor = random.choice(('c', 'g', 'b', 'k')), so the figure mixes two unseeded RNGs and its colours carry no information. 513 assignsb_sum = np.zeros(T+1)and never uses it. 498 has a space after the opening parenthesis,ax.plot( b, 'b-', label="Debt")(E201), where 496 and 497 do not. And 836 draws the impulse date as a two-point series,ax.plot((S, S), (-L, L), 'k-', lw=0.5), whereaxvline(S)is the one-call form.[qe-fig-008] — Use lw=2 for line charts. Count: 5. Lines: 496, 497, 498, 834, 835. Example: plot() without lw=.
[qe-math-002] — Use \top for transpose notation. Count: 8. Lines: 158, 394, 406, 417, 623, 669. Example: apostrophe transpose
A'.[qe-math-010 (proposed)] — Blackboard \mathbb{P}, \mathbb{E}, \mathbb{V} with braces. Count: 3. Lines: 663, 896. Example: non-blackboard
\mathrm{Var}.[qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 5. Lines: 997, 848, 935, 173, 683. Example: the footnote at 997 contradicts itself in three lines: it says ‘Suppose instead that we had imposed the following more standard assumptions on the utility function: \(u'(c) >0, u''(c)<0, u'''(c) > 0\)’, and then two sentences later ‘But the fact that \(u''' <0\) implies via Jensen’s inequality that \(\mathbb{E}_t [u'(c_{t+1})] > u'(\mathbb{E}_t [c_{t+1}])\)’ - the sign has flipped, and the flipped sign is the one the argument runs on (the inequality as stated needs \(u''' > 0\), i.e. \(u'\) convex). 848 says the same word twice with opposite meanings intended: ‘notice how most of a transitory income shock is saved and only a small amount is saved’ - the second should be ‘consumed’, and the sentence is contradicting 784-786 as written. 935 has lost its reference: ‘Application of formula to this example shows that’, where the parallel sentence at 788 reads ‘Application of the formula for debt in
{eq}`pi_ssr`to this example shows that’. 173 packs two sentences and a double space into one{note}line. And 687 states a result by describing a picture instead of showing one: ‘The impulse response function of \(\{c_t\}\) to the innovation \(\{w_t\}\) is a box’.[qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 6. Lines: 264, 270, 41, 621, 553, 856. Example: the same term is italic and bold six lines apart: ‘consumption equals economic income’ at 264, then ‘economic income equals’ at 270. The pattern runs through the lecture - the cointegration family is italic when introduced (cointegration 41, cointegrating residual 633) and bold in between (cointegrating vector 621); the innovation family likewise (innovation 97, innovation to the expected present value 553, innovation representation 856, against present value of the moving average coefficients 730). Eight definitions are bolded (random walk 91, the five wealth terms at 266-270, cointegrating vector 621, present value of the moving average coefficients 730, fundamental 1005) and twelve are italicised (state 40 and 276, cointegration 41, information set 77, innovation 97, no Ponzi scheme 176, Euler equations 196, dynamic programming 222, innovation to the expected present value 553, cointegrating residual 633, innovation representation 856, random walks 89), so the file is close to a coin flip. The three genuine italic emphases (necessary 209, is 609, permanent 922) are correct.
[qe-writing-006] — Capitalize lecture titles properly. Count: 15. Lines: 56, 107, 186, 217, 274, 331, 421, 525, 530, 635, …. Example: H2 Title Case: ‘The Savings Problem’ (Savings, Problem).
[qe-writing-008] — Remove excessive whitespace between words. Count: 29. Lines: 45, 69, 76, 91, 173, 196, 223, 269, 271, 383, …. Example: 2 spaces.
Medium severity#
[qe-fig-001] — Do not set figure size unless necessary. Count: 3. Lines: 494, 511, 826. Example: figsize=.
[qe-fig-003] — No matplotlib embedded titles. Count: 1. Lines: 833. Example: .set_title.
[qe-fig-005] — Descriptive figure names for cross-referencing. Count: 3. Lines: 474, 510, 803. Example: code-cell figure without mystnb figure metadata.
[qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 3. Lines: 259, 276, 234. Example: the same fraction is written two ways in one derivation:
{r \over 1+r}at 259 and 304 against\frac{r}{1+r}at 269, and{1 \over 1-\beta}at 560 and 584 against\frac{c_t}{1-\beta}at 249 -\overis the deprecated plain-TeX primitive and four of the file’s nine fractions use it. 276 writes the state as the row vector\begin{bmatrix} b_t & z_t \end{bmatrix}while 349-353 writes the same object as the column \(x_t = [z_t; b_t]\) with the components in the opposite order, and 623 writes a third variant,\begin{bmatrix} b_t & c_t \end{bmatrix}'. 234 puts a\fracin an exponent, \(\beta^{\frac{t}{2}}\), which renders as a two-storey fraction at superscript size; \(\beta^{t/2}\) is both simpler and legible.[qe-ref-001] — Use correct citation style. Count: 3. Lines: 858, 898. Example:
{cite}in narrative flow: ‘of{cite}’.[qe-writing-001] — Use one sentence per paragraph. Count: 4. Lines: 173, 222, 997, 1005. Example: 2 sentences in one paragraph.
[qe-writing-003] (reviewer) — Maintain logical flow. Count: 2. Lines: 896, 492. Example: 896 defines the innovation with a bare
E- ‘\(a_t := y_t - E [ y_t \,|\, y_{t-1}, \ldots, y_0]\)’ - the only bare expectation operator in a lecture that writes\mathbb{E}more than thirty times (73, 76, 114, 119, 158, 181, 193, 206, 249, 258, 293, 394, 540, 550, 560, 571-573, 630, 977, 983, 990, 997), so the definition of the object Example 2 is built on looks like a different operator from the one used everywhere else. Second, the two figures in the IID example (492-503, 510-522) draw fromnp.random.randnwith no seed, and the claims made about them are specific - ‘consumption is considerably smoother than income’ (506) and the cross-sectional fan at 508 - so both the picture and the strength of the claim change on every build; the copy of this file inlecture-python.mysthas already been converted to a seedednp.random.default_rnggenerator threaded throughtime_path.[qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 3. Lines: 683, 656, 601. Example: the section headed ‘### Impulse Response Functions’ (683-689) contains no impulse response function. It asserts the shape in prose (‘is a box’, 687) and gives the height as a formula (689), and the figure that actually draws it appears 120 lines later inside ‘### Example 1’ (803-843), after which 850 refers back to ‘The box-like impulse responses’ - so the reader meets the claim, the formula, and the picture in three different places. Second, 656-681 derives the lecture’s one testable cross-sectional prediction, \(\mathrm{Var}[c_t] = \mathrm{Var}[c_0] + t\,\hat\sigma^2\), and reports that ‘A number of different studies have investigated this prediction and found some support for it’ - while the 250-path fan at 510-522, which is exactly that prediction rendered, sits 150 lines earlier with no cross-reference; overlaying \(\pm\sqrt{\mathrm{Var}[c_0] + t\hat\sigma^2}\) on those paths would make the linear-in-\(t\) spread a visible check rather than a citation. Third, the Cointegration section (598-633) builds to ‘\((1-\beta) b_t + c_t\) is asymptotically stationary’ (619) and never plots the residual, even though the sequel
perm_income_consplots precisely this object (its 701-719) - a figure here, or a forward pointer to that one, would close the argument.
Low severity#
[qe-fig-006] — Lowercase axis labels. Count: 1. Lines: 501. Example: axis label
Time.
Strengths#
The martingale machinery is set up before it is needed and at exactly the level required: 66-105 defines a discrete-time martingale, says what the information set is and who chooses it (‘just a collection of random variables that the modeler declares to be visible at \(t\)’, 79-80), gives the gambler’s fair game as the intuition, specialises to random walks, and then closes the loop with a caution that the specialisation is strict (‘Not every martingale arises as a random walk’, 105) - so when
{eq}`sprob5`arrives at 206 the reader already knows what it claims.The optimal decision rule is derived by naming its three ingredients as a numbered list (228-232: the Euler equation, the period budget constraint, the boundary condition) and then using each one visibly -
{eq}`sprob3`gives the transversality limit at 234,{eq}`sprob2`is solved forward at 236-242, and the martingale property plus the law of iterated expectations turns{eq}`sprob6`into{eq}`sprob7`at 244-250.The lecture flags its own hard step instead of hiding it: after the four-line chain at 312-323 it says ‘To get from the second last to the last expression in this chain of equalities is not trivial’ and then supplies the two facts needed, \((1+r)\beta = 1\) and \((I - \beta A)^{-1} = \sum_j \beta^j A^j\) (325-327).
Wealth accounting is spelled out as a list of definitions with the marginal propensity to consume identified as the interest factor (264-272), which is what makes the phrase ‘consumption equals economic income’ mean something, and the final bullet gives the operational reading - ‘the amount the consumer can consume while leaving its wealth intact’.
Hall’s representation is presented as a change of state, with the consequence stated explicitly: 590-596 says the state can be taken as \((c_t, z_t)\), that ‘Debt \(b_t\) has disappeared as a component of the state because it is encoded in \(c_t\)’, and that the consumption innovation \((1-\beta) U (I-\beta A)^{-1} C w_{t+1}\) is ‘a more explicit representation of the martingale result in
{eq}`sprob5`’ - so the alternative representation earns its section.The two classic examples are built on one common endowment process (737-765, with \(z_{1t}\) permanent and \(z_{2t}\) transitory) and differ only in what the consumer observes - the state \(z_t\) in Example 1 (769), only \(y_t\) and its history in Example 2 (854) - which isolates the informational assumption, and Example 2 then derives the Kalman-gain weighting \(1 - \beta(1-K)\) and reconciles it with Example 1 by taking \(K\) to its endpoints (920-943).
Six of the seven footnotes carry real content rather than asides - the necessity of linear marginal utility for the martingale result (997), the definition of an optimal decision rule (999), \(d(L) = U(I - AL)^{-1}C\) (1001), the sufficient condition for asymptotic stationarity (1003), and the definition of a fundamental moving-average representation (1005).
Recommended actions#
Fix the sign error in footnote 997: the assumptions are \(u''' > 0\), and the Jensen step needs \(u''' > 0\), so ‘the fact that \(u''' <0\)’ should read \(u''' > 0\). As written the footnote asserts the opposite of its own premise while drawing the conclusion the premise supports.
Fix 848 (‘only a small amount is saved’ should be ‘consumed’ - as written it contradicts 784-786) and restore the missing equation reference at 935 (‘Application of formula to this example’), following the pattern at 788.
Rename the second
time_path(810). Two functions with one name 328 lines apart, with different signatures and different returns, is the kind of thing that works in a linear notebook run and fails for everyone else -impulse_pathorshock_responsewould do. While there, renameLat 829, which collides with the lag operator introduced at 705.Seed the simulations. Sync the RNG handling from
lecture-python.myst/lectures/perm_income.md, which threadsnp.random.default_rng()throughtime_path(its 482-493, 516) instead of callingnp.random.randninside a@jitfunction - the two copies differ in only seven places, three of which are that change and four of which are intersphinx prefixes that this copy has and the sibling does not, so the sync should go one way for the RNG and stay put for the links. Also droprandom.choicefor line colours at 516 and the unusedb_sumat 513.Settle bold-for-definition, italic-for-emphasis. The clearest single edit is 264 and 270, where economic income and economic income appear six lines apart; then bold the twelve italicised definitions listed in the qe-writing-005 finding and leave necessary (209), is (609) and permanent (922) as they are.
Move the impulse-response figure into the section named after it, or state at 683-689 that the picture appears in Example 1 and cross-reference it - and add a
:name:to the three figure cells (474, 510, 803) so 506, 508 and 850 can cite them instead of pointing at ‘the next figure’ and ‘the figure below’.Overlay the theoretical cross-sectional variance on the 250-path figure at 510-522:
{eq}`pi_vt`gives \(\mathrm{Var}[c_t] = \mathrm{Var}[c_0] + t\hat\sigma^2\) and \(\hat\sigma^2\) is in closed form at 669, so the prediction that 656-681 attributes to the empirical literature can be drawn on top of the simulation it predicts.Do the math sweep: eight apostrophe transposes to
^\top(158, 394, 406 x2, 417, 623, 669 x2),\mathrm{Var}to\mathbb{V}at 663 (twice on the line) and the bareE [to\mathbb{E}at 896 (qe-math-010 (proposed)), the four\overfractions to\frac(259, 304, 560, 584), and \(\beta^{\frac{t}{2}}\) to \(\beta^{t/2}\) at 234.Sentence-case the 15 Title Case headings (56, 107, 186, 217, 274, 331, 421, 525, 530, 599, 635, 683, 691, 733, 945, 957) - note the sibling copy has the same headings, so this is not an upstream-sync win and has to be done in both.
Note two things a contributor working from the report alone would get wrong. First, of the four qe-writing-001 hits, 997 and 1005 are
[^label]:footnote bodies - the already-filed false-positive class - so only the two{note}bodies at 173 and 222 need splitting. Second, qe-fig-006 reports one capitalised axis label (501) but there are three: 520 and 838 set the label throughax.set(xlabel=...), which the check cannot see, so lowercase all three and drop the pointless raw string at 838 (r'Time').Remaining sweep: 29 double spaces, the three
{cite}-as-subject constructions (858, 898), the threefigsizeoverrides (494, 511, 826),lw=2on the five unwidened line plots (496, 497, 498, 834, 835 - thelw=0.8at 517 andlw=0.5at 836 are deliberate and correct), the embedded title at 833 (which also spells ‘reponse’), and the stray double blank lines at 800-802.