perm_income_cons#
Series: lecture-dp
File:
lectures/perm_income_cons.mdAudit date: 2026-08-26
Corpus snapshot:
c30490a2f4Categories audited: writing, math, code, figures, references, links (JAX out of scope)
Overall score: 6.5 / 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 |
5.5/10 |
|
References |
10/10 |
no mechanical violations detected. |
Links |
9/10 |
|
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: 6. Lines: 553, 556, 368, 560, 598, 404. Example: 553 and 580 have a single blank line before a top-level
defwhere PEP8 asks for two (E302), and 553-554 then opens the function body with a blank line. 556 and 583 writeT = bsim.shape[1]with two spaces after=(E222). Comma spacing is inconsistent inside single statements: 368np.zeros((3,1)), 379 and 380.reshape(4,1), and 548cons_mean[t], cons_var[t] = μ_y[1,0], Σ_y[1, 1]- the same line has one subscript without the space and one with, while 345 and 355 usenp.zeros((3, 3))andnp.zeros((1, 4))(E231). 560 assignsxvals = np.arange(T)inconsumption_income_debt_figureand never uses it; the identical line at 602 in the sibling function is used six times. 598-599 are two consecutive blank lines inside a function body (E303). 404 has two spaces after the comment marker,# Form closed loop system. Alsoβ_LQ = βat 381 is an alias used once, at 396.[qe-fig-005] — Descriptive figure names for cross-referencing. Count: 6. Lines: 625, 635, 716, 759, 769, 788. Example: code-cell figure without mystnb figure metadata.
[qe-fig-008] — Use lw=2 for line charts. Count: 12. Lines: 563, 564, 565, 566, 572, 573, 605, 606, 613, 614, …. Example: plot() without lw=.
[qe-math-002] — Use \top for transpose notation. Count: 8. Lines: 244, 247, 249, 298. Example: apostrophe transpose
x'.[qe-math-010 (proposed)] — Blackboard \mathbb{P}, \mathbb{E}, \mathbb{V} with braces. Count: 2. Lines: 227, 240. Example: missing braces:
\mathbb E.[qe-writing-003] (reviewer) — Maintain logical flow. Count: 5. Lines: 289, 659, 729, 310, 399. Example: 289-293 defines a matrix and leaves a shock attached to it:
\tilde C := \begin{bmatrix} C \\ 0 \end{bmatrix} w_{t+1}- thew_{t+1}belongs to the law of motion{eq}`pilqsd`at 236, not to \(\tilde C\), and the code confirms it (CLQ = np.array([0., σ, 0., 0.]).reshape(4,1), 380). 659 mismatches its own summation index:c_0 = (1-\beta) E_0 \sum_{t=0}^\infty \beta^j y_{t}sums over \(t\) while the exponent runs on \(j\), and it is derived one line below{eq}`old12`at 653, which gets it right. \(R\) carries two meanings: the LQ criterion matrix at 228, 301 (‘\(R :=\) a \(4 \times 4\) matrix of zeros’) and 310 (‘a small entry \(\epsilon > 0\) in the \((4,4)\) position of \(R\)’), and the gross interest rate at 729 (‘the fixed gross interest rate \(R = r+1\)’), 747 and 751 (‘\(R = \beta^{-1}\)’) - the code has to disambiguate them asRandRLQ(336, 373) and then prints the matrix under the wrong name,print(f"R = \n {RLQ}")at 389. 310-314 introduces the penalty trick, does not say what \(\epsilon\) is (the code uses1e-9at 376), and promises ‘We’ll check whether it really is small numerically soon’ - the comparison at 457-466 is presumably that check, but it never says so and 466 only asserts ‘We have verified that the two methods give the same solution’. And 399 is not a sentence: ‘We’ll save the implied optimal policy function soon compare them with what we get by employing an alternative solution method’.[qe-writing-006] — Capitalize lecture titles properly. Count: 12. Lines: 137, 165, 201, 221, 253, 328, 407, 472, 494, 508, …. Example: H3 Title Case: ‘Digression on a Useful Isomorphism’ (Useful, Isomorphism).
[qe-writing-008] — Remove excessive whitespace between words. Count: 19. Lines: 53, 55, 89, 149, 156, 157, 174, 203, 219, 296, …. Example: 2 spaces.
Medium severity#
[qe-fig-001] — Do not set figure size unless necessary. Count: 3. Lines: 559, 601, 707. Example: figsize=.
[qe-fig-003] — No matplotlib embedded titles. Count: 3. Lines: 568, 609, 711. Example: .set(title=.
[qe-link-002] — Use doc links for cross-series references. Count: 1. Lines: 163. Example: raw link to python-advanced.quantecon.org.
[qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 2. Lines: 247, 228. Example: 247 writes the trace as
{\rm trace} ( P \tilde C \tilde C'), the plain-TeX{\rm ...}form, where\operatorname{tr}is the one-token standard and is the only occurrence of\rmin the file. Second, the transpose prime is placed three different ways within seventy lines: before the subscript at 228 and 240 (x'_t R x_t,u'_t Q u_t,\mathbb E w_t w'_t), on a bare symbol at 244 and 247 (x'Px,\tilde C'), and after the subscript at 298 (x_t' R x_t + u_t'Q u_t) - the last of which is also the placement used by the lecture this one builds on. The prose calls the observation matrix \(U\) (131, 198, 273, 423-425) while the code calls itG(341, 433-434, 445) with no note that they are the same object, which then propagates intoG_LSS(447).[qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 4. Lines: 745, 210, 685, 666. Example: 745-755 says one thing five times, in five consecutive one-sentence paragraphs: ‘This rigs a closed economy in which people are borrowing and lending with each other at a gross risk-free interest rate of \(R = \beta^{-1}\)’ (745-747), ‘Across the group of people being analyzed, risk-free loans are in zero excess supply’ (749), ‘We have arranged primitives so that \(R = \beta^{-1}\) clears the market for risk-free loans at zero aggregate excess supply’ (751), ‘So the risk-free loans are being made from one person to another within our closed set of agents’ (753), ‘There is no need for foreigners to lend to our group’ (755). 210-212 is a rhetorical question whose answer is content-free - ‘Why solve a model in two distinct ways? / Because by doing so we gather insights about the structure of the model’ - and 214-219 then gives the real answer, so the first exchange can go. 685-689 is 40 words that end in ‘rise pretty much together’ after already saying ‘both rise markedly’. 666 drifts pronouns inside one sentence: ‘he or she incurs so much debt that eventually … he consumes less each period than his nonfinancial income’. Separately, the lecture’s central term is spelled three ways - ‘co-integrating relationship’ (648), ‘cointegrating residual’ (679, 691, 695, 784) and ‘Cointegration’ (676, 711).
[qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 3. Lines: 642, 776, 308. Example: the lecture renders six figures from three plotting functions, none of them named, and then refers to all six by position: ‘the above graphs’ (642), ‘the accompanying graph’ (670), ‘The following figure’ (678), ‘the corresponding figures’ (757), ‘The graphs confirm the following outcomes’ (776), ‘the cointegration figure’ (786). The claims at 776-784 are explicitly comparative - ‘As before, the consumption distribution spreads out over time’, ‘Unlike before, the average level of debt stays at zero’, ‘Now the cointegrating residual seems stationary, and not just asymptotically stationary’ - and each requires the reader to hold a figure from 150 lines earlier in their head, because there is no
:name:to cross-reference and no side-by-side. The two cointegration figures (717 and 789) are the payoff of the whole lecture and are drawn in separate cells 70 lines apart; one two-panel figure with the transient case on the left and the Bewley case on the right would make the asymptotically-stationary-versus-stationary distinction visible instead of remembered. Second, in 791 lines there is not one admonition, and 303-314 is exactly what one is for: ‘there is one problem remaining … we’re going to use a trick: put a small penalty on \(b_t^2\) … That will induce a (hopefully) small approximation error’.
Low severity#
None found.
Strengths#
The lecture solves the same model twice and then checks the two solutions against each other in code rather than asserting agreement: 454 states the invariant (‘
A_LSScalculated as we have here should equalABFcalculated above using the LQ model’), 457 prints the differenceABF - A_LSS, and 463 printsc_polagainst-F- so the claim at 466 is executable, and a reader who changes a parameter finds out immediately if it stops holding.The isomorphism with Barro tax smoothing is set out as an explicit four-line dictionary (152-159: \(T_t \leftrightarrow c_t\), \(G_t \leftrightarrow y_t\), \(B_t \leftrightarrow b_t\), \(R \leftrightarrow 1+r\)) and then the consequence is stated precisely - ‘All characterizations of a \(\{c_t, y_t, b_t\}\) … automatically apply to a \(\{T_t, G_t, B_t\}\) process’ (161) - rather than left as a remark that the two models are ‘similar’.
The two experiments differ in exactly one input and the lecture says so before running them: 476-478 (‘The examples differ only in the initial states with which we endow the consumers. All other parameter values are kept the same’), and the difference is then a single argument swap in the call -
μ_0, Σ_0at 626 againstmxbewley, sxbewleyat 760 - with those two initial conditions built and commented back at 344-362.The tilde notation is introduced with its reason attached: 242 says ‘The tildes in \(\tilde A, \tilde B, \tilde C\) are to avoid clashing with notation in
{eq}`sprob15ab2`’, which is the kind of housekeeping usually left implicit, and it makes the mapping at 277-294 readable against the income process at 186-198.The economics of the two initial conditions is drawn out rather than left in the figures: 723-736 explains the first as a small open economy borrowing from abroad in anticipation of rising income, and 738-743 rigs the second as a closed Bewley economy by drawing \((y_{-1}, y_{-2})\) from the invariant distribution - so the change from ‘debt heads north’ to ‘average debt stays at zero’ (783) is understood before it is seen.
The cointegration point is made carefully and with the right qualifier: 691-693 states that ‘the definition of cointegration implies that the cointegrating residual is asymptotically covariance stationary, not covariance stationary’, and 695-699 then attributes the visible transient to the initial condition and announces the second example as the controlled comparison.
Recommended actions#
Fix the two mathematical errors first, since both are in equations a reader will try to reproduce: strip the trailing
w_{t+1}from the definition of \(\tilde C\) at 289-293, and correct the summation at 659 to\sum_{j=0}^\infty \beta^j y_jto match{eq}`old12`at 653.Rename one of the two \(R\)’s. The criterion matrix (228, 301, 310) and the gross interest rate (729, 747, 751) share a symbol, the code already keeps them apart as
RLQandR(336, 373), and line 389 prints the matrix labelledR =, which is the collision made visible. Either rename the prose interest rate to \(1+r\) throughout (the lecture already uses that form at 157 and 251) or rename the criterion matrix.Convert the raw link at 163 to a
{doc}link:https://python-advanced.quantecon.org/smoothing.htmlpoints at a lecture that is in this series (lecture-dp/lectures/smoothing.md), so it should be{doc}consumption and tax smoothing models`` - this is the qe-link-002 finding and the target is closer than the report implies. While there, prefix the cross-book {ref}at 502:lln_mris a label inlln_clt, which does not exist inlecture-dp(it is inlecture-python-introandlecture-python.myst), whereas every{doc}in this file is correctly prefixed (intermediate:linear_modelsat 62 and 174,intermediate:aiyagariat 68). Neither link rule reports an unprefixed cross-book{ref}, so this one has to be caught by eye.Sentence-case the 12 Title Case headings (137, 165, 201, 221, 253, 328, 407, 472, 494, 508, 676, 721) - ‘Digression on a useful isomorphism’, ‘A specification of the nonfinancial income process’, ‘The LQ approach’, ‘The LQ problem’, ‘Mapping into the LQ framework’, ‘The exogenous nonfinancial income process’, ‘Comparison with the difference equation approach’, ‘Two example economies’, ‘First set of initial conditions’, ‘Population and sample panels’, ‘Cointegration residual’, ‘A “borrowers and lenders” closed economy’. Note that the H1 at 23 is correctly Title Case and should be left alone.
Name the six figures. Add
mystnbfigure: name/captionmetadata to the six figure-producing cells (625, 635, 716, 759, 769, 788), move the three embedded titles into those captions (568, 609, 711), and then replace the six positional references in the prose (642, 670, 678, 757, 776, 786) with{numref}citations - this is what makes the four comparative claims at 776-784 checkable.Convert the eight apostrophe transposes to
^\top(244, 247, 249, 298 - the report’s four lines cover eight sites) and pick one prime placement while doing it; brace the two bare\mathbb Eat 227 and 240, and convert the five bareE_0/E_tat 94, 115, 653, 659 and 683 to\mathbb{E}at the same time - the checker does not report those five, because its bare-letter branch needs a bracket or brace immediately after the E, so working from the count of 2 alone leaves the file inconsistent.Tidy the code per the findings above (E302 at 553 and 580, the double
=spaces at 556 and 583, the comma spacing at 368, 379, 380 and 548, the deadxvalsat 560, the double blank line at 598), drop the threefigsize=(10, 8)overrides (559, 601, 707), and addlw=2to the 12 line plots (563-614) - unlikelqcontrol, none of these calls sets a width through**kwargs, so all 12 are real.Cut the repetition: collapse 745-755 to one or two sentences, delete the rhetorical exchange at 210-212, settle on one spelling of ‘cointegrating’ (648 has the only hyphen), fix 399, and clear the 19 double spaces. Note that this file and
lecture-python.myst/lectures/perm_income_cons.mddiffer only in the three intersphinx prefixes at 62, 68 and 174 - the two copies carry identical style findings, so every fix here has to be mirrored, and this copy is the one that is ahead on links.