money_inflation_nonlinear#

  • Series: lecture-python-intro

  • File: lectures/money_inflation_nonlinear.md

  • Audit date: 2026-08-26

  • Corpus snapshot: a12d17c0ef

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

  • Overall score: 7.9 / 10

  • Priority: HIGH

Score breakdown#

Category

Score

One-line note

Writing

3/10

qe-writing-006 ×6; qe-writing-005 ×4; qe-writing-003 ×3, +4 more.

Math

9.5/10

qe-math-009 ×3.

Code

6.5/10

qe-code-001 ×5; qe-code-002 ×3.

JAX

out of scope

JAX rules target lecture-jax.

Figures

6.5/10

qe-fig-005 ×4; qe-fig-004 ×2; qe-fig-008 ×9, +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-code-001] (reviewer) — Follow PEP8 unless closer to mathematical notation. Count: 5. Lines: 128, 273, 385, 500, 666. Example: 273 assigns a lambda to a name - eq_g = lambda x: np.exp(-model.α * x) - np.exp(-(1 + model.α) * x) - where PEP8 asks for a def, and the file already has compute_seign (175) computing exactly that expression, so the lambda is both a style violation and a duplicate. Three continuation lines miss their visual indent: CaganLaffer = namedtuple('CaganLaffer', opens its parenthesis at column 24 and continues at column 24 (128-132, one short), line_params = {'lw': 1.5, opens its brace at column 14 and continues at 14 (408-410, one short), and ax.text( at 384 opens at column 19 and continues at column 25 (five past). 132 also leaves a space before the closing bracket, "g" ]). 500 runs to 90 characters. And the exercise cells pad before = to align blocks of assignments at 666-667, 692-694 and 722-724 (x_star_α  =, g_max_α   =, m_temp  =, p0_l_α  =), which PEP8 lists under Pet Peeves. One item that is not spacing: 492 imports minimize_scalar in the middle of the lecture when 115-120 is the import cell.

  • [qe-fig-008] — Use lw=2 for line charts. Count: 9. Lines: 188, 362, 365, 368, 371, 509, 585, 586, 668. Example: plot() without lw=.

  • [qe-writing-006] — Capitalize lecture titles properly. Count: 6. Lines: 48, 78, 162, 206, 278, 339. Example: H2 Title Case: ‘The Model’ (Model).

  • [qe-writing-008] — Remove excessive whitespace between words. Count: 37. Lines: 20, 27, 29, 33, 38, 40, 46, 82, 106, 108, …. Example: 2 spaces.

Medium severity#

  • [qe-code-002] — Use Unicode symbols for Greek letters in code. Count: 3. Lines: 664, 691, 717. Example: spelled-out alpha.

  • [qe-fig-004] — Caption formatting conventions. Count: 2. Lines: 166, 394. Example: caption of 15 words.

  • [qe-fig-005] — Descriptive figure names for cross-referencing. Count: 4. Lines: 504, 568, 659, 713. Example: code-cell figure without mystnb figure metadata.

  • [qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 3. Lines: 85, 451, 464. Example: the steady-state inflation rate carries four names and the lecture never says they are the same object. 80 announces “the two prospective limiting values for \(\overline \pi\)”; 85-88 introduces \(x\) as “a common rate of growth of logarithms of the money supply and price level” and states the defining equation {eq}`eq:steadypi` in \(x\); 155-157 solves it and stores the answers as \(\pi_l\) and \(\pi_u\) (labelled that way on the figure at 170 and 190); and the exercises add \(x^*\) (457) while mni_ex2 reverts to \(\pi_l(g), \pi_u(g)\) (553-554) with its own solution labelling the axis “steady-state inflation rate \(x\)” (516, 673). Not one sentence connects \(x\) to \(\overline\pi\) to \(\pi_l\). The same function is spelled three ways too: \exp(-\alpha x) - \exp(-(1+\alpha)x) at 93 and 99, \(L(x) = e^{-\alpha x} - e^{-(1+\alpha)x}\) at 451, and compute_seign in code at 175 - so the reader matching the exercise’s \(L\) against the body’s display has to notice they are the same. And the lower/upper distinction is marked two different ways in adjacent paragraphs: \(\underline p(m_0)\) and \(\overline p(m_0)\) by under- and overline (208-213), \(\pi_l\) and \(\pi_u\) by subscript. {\rm max} for a subscript recurs at 464, 468, 511, 553, 558, 609, 641 and 669 where \mathrm{max} or _{\max} is the LaTeX form.

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

  • [qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 3. Lines: 29, 33, 421. Example: 29-33 takes three sentences to say “as in the linear lecture”, and the middle one is circular in the same way its predecessor was: “That lecture presented a linear version of the model in this lecture” (31). The third then puts the lecture’s own contents in the past tense - “As in that lecture, we discussed these topics:” (33) - for four topics that are discussed below, not above. The same material then arrives a second time at 417-439, where 425-427’s {note} restates 29-31 and the bullets restate 35-46. 421 is a 78-word single sentence with two quoted terms and a colon, copied verbatim from {doc}`money_inflation`:962, and it is the longest sentence in a lecture whose house style is otherwise one short sentence per paragraph. Smaller: 27 opens a quotation with two apostrophes, ''method 2'', which renders as two straight marks rather than a quote, and the two +++ {"user_expressions": []} markers at 123 and 141 are leftover jupytext metadata in the published source.

  • [qe-writing-003] (reviewer) — Maintain logical flow. Count: 3. Lines: 106, 417, 429. Example: 104-106 says the same thing about both sides of the same equation. {eq}`eq:steadypi` is \(\exp(-\alpha x) - \exp(-(1+\alpha)x) = g\); 104 correctly calls the left side “steady state revenue raised by printing money”, and then 106 calls the right side “the quantity of time \(t\) goods that the government raises by printing money” - which is the left side again. The counterpart passage in {doc}`money_inflation` (its lines 212-214) gets it right: left side seigniorage, right side government expenditures. 417 is a broken sentence with a full stop in the middle: “almost all paths converge to the higher inflation tax rate displayed in the stationary state Laffer curve. displayed in figure {numref}`laffer_curve_nonlinear`.” And 429 opens “We discovered that” with a bulleted conclusion, immediately after a {note} about the linear lecture and under no heading of its own - the material is lifted from the “Equilibrium selection” section of {doc}`money_inflation` without the section, so a reader reaching 429 has just been told about a different model and is then handed “we discovered”.

  • [qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 4. Lines: 82, 284, 300, 446. Example: no term in this lecture is bolded at its definition. 35-36 bolds inflation tax and Laffer curve in the Overview, both imported from {doc}`money_inflation` and neither defined here; after that, bold is used only for structural labels - Pseudo-code (300), the three exercise titles (446, 548, 631) and the Part a. / Part b. / Part c. markers (476, 523, 596, 612, 657, 684, 711). Meanwhile the definitions that do occur are marked three other ways: steady state is italicised at 82, and 284 sets two concepts in inline code - “we’ll treat \(m_t\) as a natural state variable and \(p_t\) as a jump variable” - which renders them as monospace, the formatting reserved for identifiers; the counterpart sentence in {doc}`money_inflation` (line 795) bolds state and jump correctly. Names are italicised too, at 18 (Laffer curves) and 280 (Method 2).

  • [qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 3. Lines: 191, 368, 382. Example: {numref}`p0_path_nonlin` (394-415) plots every path with **line_params and no label, and draw_iterations never calls ax.legend(), so the figure shows however many curves np.arange(p0_l, p0_u, 0.1) happens to produce with nothing identifying any of them. Worse, the \(\pi_l\) and \(\pi_u\) reference lines are drawn only on the bottom two panels - for ax in axes[2:] at 382 - while 417 tells the reader to stare at “the paths of price levels” to see the convergence, i.e. at the second panel, which carries no marker for either steady state. On the Laffer-curve figure the two labels are placed at plt.gca().get_ylim()[0]*2 (191), a y-coordinate defined as twice the lower axis limit: for a curve whose values sit just above zero that is an arbitrary height, and it lands off the axes whenever the lower limit is negative, where ax.annotate with xycoords=('data', 'axes fraction') would pin them. Two smaller gaps: the pseudo-code at 300-336 is thirty-six lines of nested bullets restating a two-line recursion that the two displayed equations already give, and mni_ex3 part a asks for the closed form \(g_{\max}(\alpha) = \alpha^\alpha/(1+\alpha)^{1+\alpha}\) (641) which its own solution never evaluates - 667 computes compute_seign(x_star_α, α) instead and the two are never compared.

Low severity#

  • [qe-fig-001] — Do not set figure size unless necessary. Count: 1. Lines: 348. Example: figsize=.

Strengths#

  • The lecture is honest about what the non-linearity costs and says so in the one place a reader would otherwise be confused: the {prf:remark} at 71-74 points out that {eq}`eq:mdemand` is linear in logs while {eq}`eq:msupply` is linear in levels, and that this is what forces the computation method to change - which is the whole reason this lecture exists alongside {doc}`money_inflation`.

  • The claim that a particular \(p_0\) pins the inflation rate forever is verified in code rather than asserted: 262-276 simulates 150 periods from p0_l and then checks three separate things - that \(\pi\) and \(\mu\) agree at the end, that the log increments of \(m\) and \(p\) are equal, and that the resulting increment satisfies the steady-state equation \(\exp(-\alpha x) - \exp(-(1+\alpha)x) = g\) to np.isclose.

  • The pseudo-code at 302-336 is written as a mapping from \((m_t, p_t)\) to \((m_{t+1}, p_{t+1})\) with the equation to solve named at each step, and the implementation at 247-259 follows it line for line, including the correctly inverted form \(p_{t+1} = \lambda^{-1}p_t + (1-\lambda^{-1})m_{t+1}\) that 310 derives from {eq}`eq:mdemand2`.

  • The three exercises form a genuine progression and each one ends somewhere the body does not: mni_ex1 derives the analytic peak \(x^* = \ln((1+\alpha)/\alpha)\) and cross-checks it against minimize_scalar (491-501); mni_ex2 traces both roots as \(g\) varies and states the two limits (\(\pi_l \to 0\), \(\pi_u \to \infty\) as \(g \to 0^+\); the roots merging at \(x^*\)) with a numerical check of each (598-610); mni_ex3 varies the money-demand elasticity and finds a genuinely new result - that \(\alpha = 0.8\) pushes \(g_{\max}\) below the benchmark deficit, so no steady state exists (706-709).

  • mni_ex3 part c is the right closing experiment: it starts each feasible \(\alpha\) from the midpoint of \([\underline p, \overline p]\) and shows every path converging to \(\pi_u\) (713-741), which establishes that the perverse dynamics are not an artefact of the benchmark calibration - a claim the body makes only for one \(\alpha\).

  • The Laffer-curve figure (166-203) does what the corresponding figure in {doc}`money_inflation` does not: it marks both stationary inflation rates with labelled vertical lines and draws the deficit \(g\) as a horizontal line, so the two intersections the whole lecture turns on are visible in the figure the prose cites.

  • The lecture reports zero mechanical Math and Code violations, and that is earned rather than accidental - Greek identifiers throughout (α, λ, π_l, π_u, μ_seq), bmatrix never needed, no \tag, no bold vectors, and every display either labelled and cited ({eq}`eq:mdemand`, {eq}`eq:msupply`, {eq}`eq:steadypi`, {eq}`eq:revmax`, {eq}`eq:mdemand2`) or genuinely used once.