perm_income_cons#

  • Series: lecture-python.myst

  • File: lectures/perm_income_cons.md

  • Audit date: 2026-08-26

  • Corpus snapshot: e25fdf2345

  • Categories 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

qe-writing-006 ×12; qe-writing-003 ×6; qe-writing-002 ×5, +3 more.

Math

4/10

qe-math-002 ×8; qe-math-010 (proposed) ×2; qe-math-009 ×4.

Code

7.5/10

qe-code-001 ×6.

JAX

out of scope

JAX rules target lecture-jax.

Figures

5.5/10

qe-fig-005 ×6; qe-fig-003 ×3; qe-fig-008 ×12, +1 more.

References

10/10

no mechanical violations detected.

Links

9/10

qe-link-002 ×1.

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: 368, 404, 553, 556, 560, 598. Example: 368, 379, 380, 548 and 549 omit the space after a comma - np.zeros((3,1)), .reshape(4,1) twice, μ_y[1,0], μ_x[3,0] (E231) - while the same cells write np.zeros((3, 3)) and Σ_y[1, 1] correctly two lines away. 556 and 583 both write T =  bsim.shape[1] with two spaces after the operator (E222). 553 and 580 begin a top-level def one blank line after the previous function’s return rather than two (E302). 598-599 leaves two blank lines inside a function body (E303). 560 assigns xvals = np.arange(T) in consumption_income_debt_figure and never uses it, and 593 assigns dmean = np.mean(debt_mean) and never uses it, while its partner cmean at 586 is used at 610 (F841 twice). 404 writes the inline comment as #  Form closed loop system with two spaces after the hash (E262). And 334 names the AR coefficients ρ1, ρ2 without underscores, against \(\rho_1,\rho_2\) in the algebra at 170 and against the underscore convention the rest of the file keeps (μ_z, Σ_z, β_LQ, A_LSS, C_LSS).

  • [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-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 5. Lines: 51, 210, 664, 666, 745. Example: 745-755 spends five consecutive one-sentence paragraphs on one fact: “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}\)” / “Across the group of people being analyzed, risk-free loans are in zero excess supply” / “We have arranged primitives so that \(R = \beta^{-1}\) clears the market for risk-free loans at zero aggregate excess supply” / “So the risk-free loans are being made from one person to another within our closed set of agents” / “There is no need for foreigners to lend to our group”. 664-674 does the same for the debt build-up, with 670-674 restating 664-666. 51-57 states the Barro isomorphism in four sentences, and then 137-163 restates the whole thing as its own subsection, with 161 repeating 55. 210-212 asks a question and answers it with nothing: “Why solve a model in two distinct ways? / Because by doing so we gather insights about the structure of the model” - the real answer follows at 214-219 and would stand on its own. And 666 changes pronoun mid-sentence: “he or she incurs so much debt that eventually, in the stochastic steady state, he consumes less each period than his nonfinancial income”.

  • [qe-writing-003] (reviewer) — Maintain logical flow. Count: 6. Lines: 341, 399, 482, 500, 659, 678. Example: 659 is broken: “So at time \(0\) we have \(c_0 = (1-\beta) E_0 \sum_{t=0}^\infty \beta^j y_{t}\)” - the sum runs over \(t\) but the discount factor is \(\beta^j\) and the summand is \(y_t\), so the index does not close. It is meant to be the \(t=0\) instance of {eq}`old12` six lines above, which has \(\sum_{j=0}^\infty \beta^j y_{t+j}\) correctly. 341 renames a matrix silently: the algebra writes the observation equation \(y_t = U z_t\) with \(U = [0\ 1\ 0]\) (131, 198, 423-425) and the code calls the same object G (341, 433-434, 445), with no sentence connecting the two. 482 says the second economy draws initial income “from the invariant distribution of financial income” - it is nonfinancial income, as 743 says correctly, and the distinction between the two is the lecture’s subject. 500 promises “the population distribution that we extract from the LinearStateSpace instance LSS”: there is no LSS, the cell at 505 builds a lss, and that object is then never used, because income_consumption_debt_series constructs its own instance at 525 - so 505 is a dead cell described by a sentence naming a variable that does not exist. 678 says “The following figure plots realizations of the left side of {eq}`old12`” and then four paragraphs intervene, including the figure’s own interpretation at 685-689, before the plotting function appears at 701. And 399-400 is ungrammatical with a dangling pronoun: “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: 4. Lines: 247, 273, 653, 729. Example: 247 writes the trace as {\rm trace}(P \tilde C \tilde C') where \operatorname{tr} or \mathrm{tr} is the standard form and shorter. 273 defines \(U_\gamma := \begin{bmatrix}\gamma & 0 & 0\end{bmatrix}\), whose subscript implies a family of \(U\)’s that does not exist - \(U = [0\ 1\ 0]\) selects \(y_t\) from the state, \(U_\gamma = [\gamma\ 0\ 0]\) selects the constant, so the two vectors have nothing in common but their shape. \(R\) carries two meanings: the LQ criterion matrix at 228, 298, 301 and 310, and the gross interest rate at 729, 747 and 751 - and even the interest-rate meaning is written two ways, “\(R = r+1\)” (729) and “\(R = \beta^{-1}\)” (747, 751), with the code adding a third name, RLQ (373), to disambiguate what the prose does not. And the annuity value \((1-\beta) E_t \sum_{j=0}^\infty \beta^j y_{t+j}\) is spelled out in full four times (653, 659, 683, 687) and described in words twice more (662, 681-683) instead of being given a name once - naming it would shorten the whole 648-699 argument.

  • [qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 4. Lines: 648, 693, 724, 736. Example: the file bolds only two terms in 791 lines, and they are the same term spelled two ways: co-integrating relationship at 648 and cointegrating residual at 679, with “cointegration” unhyphenated again at 676, 711 and 786. Meanwhile emphasis is carried by double quotes rather than italic in six places - debt “head north” (724), a “small open economy” (728), “foreigner lenders” (736), a “poor man’s Bewley model” (738), the section title “Borrowers and Lenders” (721), and “stochastic steady state” is left unmarked at 666. 693 italicises both halves of a contrast, “the cointegrating residual is asymptotically covariance stationary, not covariance stationary”, so the reader cannot tell which italic is the emphasis and which is the term. The four ex-ante italics (481, 483, 670, 780) are the file’s only correct use of the marker.

  • [qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 4. Lines: 137, 568, 646, 776. Example: the six figures come from three functions called twice each, and the titles are hardcoded inside those functions - “Nonfinancial Income, Consumption, and Debt” (568), “Consumption/Debt over time” (609), “Cointegration of Assets and Consumption” (711) - so the transient economy’s figures (630, 636, 717) and the Bewley economy’s figures (764, 770, 789) are pixel-for-pixel identically labelled and nothing on the page says which is which. That matters because 776-784 asks the reader to compare them (“Unlike before, the average level of debt stays at zero”) across 150 lines of intervening text, with no side-by-side panel and no marker on either figure. 646 and 674 describe the transient in words - “\(y_t\) starts far below its stationary mean \(\mu_{y,\infty}\) and rises early in each simulation” - without drawing \(\mu_{y,\infty}\) on the income panel, although μ_y is already computed at 347. 137-163 presents a four-way variable dictionary between two models as four long bulleted sentences where a two-column table is the natural form and is the section’s entire content. And the file has no admonition at all, though 303-314 introduces a deliberate approximation - a penalty \(\epsilon\) standing in for the no-Ponzi condition, “a (hopefully) small approximation error” - which is exactly what a {note} is for.

Low severity#

None found.

Strengths#

  • The lecture solves one model twice and then checks the two answers against each other in code rather than asserting agreement: 457 prints ABF - A_LSS, the closed-loop transition matrix from the LQ solution minus the one assembled from the difference-equation formulas at 433-447, and 463 prints c_pol beside -F, so the claim at 466 is visible in the output.

  • The two economies differ in exactly one input and the lecture arranges the code so that this is verifiable: 476-490 states that only the initial state changes, 626 passes μ_0, Σ_0 (everyone at zero income and zero debt) and 760 passes mxbewley, sxbewley, built at 350-362 out of the stationary moments computed at 347 - and the same three plotting functions are reused unchanged, so every difference across the six figures is attributable to the initial condition.

  • 303-314 is candid about an approximation instead of burying it: it says outright that the no-Ponzi condition {eq}`old42` cannot be expressed inside the LQ framework, names the workaround (a small \(\epsilon\) in the \((4,4)\) entry of \(R\), implemented as 1e-9 at 376), calls it a trick, and concedes the induced error is only “hopefully” small.

  • The economics of the first economy is derived rather than read off the plot: 648-668 goes from the cointegrating relationship {eq}`old12` to \(c_0\) as an annuity on expected discounted income, then to heavy early borrowing, then to a steady state in which consumption sits below nonfinancial income, and finally to the interest service that accounts for the gap.

  • The fan charts at 580-620 put the analytic 90% and 95% cross-sectional bands, taken from the moment_sequence() generator at 547-549, behind the 25 simulated paths on the same axes - so the population distribution and the sample are compared at every date, which is exactly what 502 says the exercise is for.

  • 137-163 makes the Barro isomorphism operational rather than gestural: four explicit variable substitutions (\(T_t \leftrightarrow c_t\), \(G_t \leftrightarrow y_t\), \(B_t \leftrightarrow b_t\), \(R \leftrightarrow 1+r\)), the consequence stated at 161, and a pointer to the advanced lecture that exploits it.