money_inflation#
Series: lecture-python-intro
File:
lectures/money_inflation.mdAudit date: 2026-08-26
Corpus snapshot:
a12d17c0efCategories 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 |
4.5/10 |
|
Math |
9.5/10 |
|
Code |
7.5/10 |
|
JAX |
out of scope |
JAX rules target |
Figures |
6/10 |
|
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: 263, 272, 291, 512, 1068. Example: 263 places
from collections import namedtupleafter an executable statement (plt.rcParams['figure.dpi'] = 300at 262), where PEP8 asks for all imports at the top of the block. Two continuation lines are under-indented against their opening delimiter:MoneySupplyModel = namedtuple("MoneySupplyModel",opens its parenthesis at column 29 and continues at column 24 (272-274), andline_params = {'lw': 1.5,opens its brace at column 14 and continues at column 14 (512-514), one short of the visual indent. 291 mixes operator spacing inside one expression and leaves a stray double space -return -γ2/R + (γ1 + γ2) - γ1 * R, with/unspaced and*spaced - and 495-501 does the same (γ1 - γ2/R0next toγ1/γ2 - (1 / γ2) * b_values[0]). Five lines run past 79 characters (1049, 1068, 1135, 1140, 1142), 1068 at 96. And LaTeX in matplotlib labels is written with doubled backslashes in ordinary strings at 1048, 1051, 1126, 1211 and 1212 while 518 correctly uses a raw string for the same job.[qe-fig-008] — Use lw=2 for line charts. Count: 8. Lines: 329, 905, 908, 912, 1048, 1121, 1122, 1209. Example: plot() without lw=.
[qe-writing-008] — Remove excessive whitespace between words. Count: 89. Lines: 35, 36, 43, 50, 54, 60, 78, 80, 81, 96, …. Example: 3 spaces.
Medium severity#
[qe-fig-001] — Do not set figure size unless necessary. Count: 4. Lines: 262, 328, 525, 889. Example: style override.
[qe-fig-004] — Caption formatting conventions. Count: 3. Lines: 313, 557, 930. Example: caption of 32 words.
[qe-fig-005] — Descriptive figure names for cross-referencing. Count: 3. Lines: 1038, 1108, 1203. Example: code-cell figure without mystnb figure metadata.
[qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 3. Lines: 223, 807, 854. Example: 223 introduces \(\overline R\) as the number 1 - “\(\bar R \in [\frac{\gamma_2}{\gamma_1}, 1] \equiv [\underline R, \overline R]\)” - while \(\bar R\) is the steady-state rate of return that ranges over that interval, and
\bar Rand\overline Rrender identically. So the constraint at 226-229 reads on the page as \(R_t \in [\underline R, \bar R]\), with the same overbarred \(R\) as both the variable and the upper endpoint. Second, the eigendecomposition section carries four notations for elements of two matrices in fifty lines: \(Q_{ij}\) for an element of \(Q\) (688-698), \(Q^{ij}\) for an element of \(Q^{-1}\) (816-820), \(Q^{\{2\}}\) for the second row of \(Q^{-1}\) (807-810), and thenQ^{-1}_{11}at 854 and 877 for the scalar reciprocal \(1/Q_{11}\) - which under the file’s own \(Q^{ij}\) convention reads as element \((1,1)\) of \(Q^{-1}\), a different number, and which 698 and 861 write the other way round as \(Q_{11}^{-1}\). A superscript that means “of the inverse matrix” cannot coexist with a superscript that means “reciprocal”. Third,{\rm max}appears as a subscript at 235, 241, 1008, 1013-1014, 1033 and 1049 where\mathrm{max}or_{\max}is the LaTeX form.[qe-writing-001] — Use one sentence per paragraph. Count: 1. Lines: 410. Example: 2 sentences in one paragraph.
[qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 4. Lines: 18, 352, 951, 962. Example: the construction “this lecture
{doc}`X`” is used seven times to mean that other lecture - “the model in this lecture{doc}`cagan_ree`” (18), “studied in this lecture{doc}`eigen_I`” (43), “this lecture{doc}`money_inflation_nonlinear`” (52 and again in the note at 968), “this lecture{doc}`laffer_adaptive`” (54), “this lecture{doc}`unpleasant`” (62) - while the reader is inside “this lecture”, so every one of them momentarily points at the wrong document. 951-969 restates the Overview: the two bullets at 955-956 are copied word for word from 47-48, and the{note}at 967-969 is line 52 verbatim. The two computation strategies are each described twice at full length - “### Method 1” (360-386) and then “## Computation method 1” (441-458), “### Method 2” (408-439) and then “## Computation method 2” (574-643) - with the same equations restated and the \(\gamma_0\) typo introduced only in the second pass. 352 is an orphan: “Now let’s compute the maximum steady-state amount of seigniorage … and the state-state rate of return on money that attains it” is followed immediately by a new section heading, and the computation it announces was already done at 300-302. And 962 is a 78-word sentence with two nested quoted terms and two{numref}references.[qe-writing-003] (reviewer) — Maintain logical flow. Count: 4. Lines: 455, 665, 760, 867. Example: 665-667 and 772 contradict each other, and the code inherits the contradiction. 665-667 states \(\Lambda = \mathrm{diag}({R_\ell}^{-1}, {R_u}^{-1})\), so with \(R_\ell < R_u\) (308-310) the first diagonal entry is the larger eigenvalue. 767-780 then argues that unless \(y^*_0 = [y^*_{1,0}, 0]^\top\) - i.e. unless the second component is killed - the path grows at \({R_\ell}^{-1}\), and concludes there is a unique \(y_0\) “for which both components forever grow at the lower rate \({R_u}^{-1}\)”. Killing the second component leaves the first mode, which under 665-667 is \({R_\ell}^{-1}\), not \({R_u}^{-1}\); so one of the two statements has the eigenvalues the wrong way round. The code then relies on opposite orderings of one
np.linalg.eigcall (672): 678-679 readsR_l = 1 / Λ[0], which needs \(\Lambda_0\) to be the larger eigenvalue, while 867 readsp0_bar = (Q[1, 0]/Q[0, 0]) * msm.M0, taking the first column of \(Q\), which needs \(\Lambda_0\) to be the smaller one forp0_barto be the low-inflation selection. Exactly one of the two is wrong for any given LAPACK ordering, and neither is guarded. The lecture’s own data says which:p0s = [p0_bar, 2.34, 2.5, 3, 4, 7, 30, 100_000]at 940 is an increasing list, sop0_baris expected just under 2.34, matching the Method 1 valuemsm.M0 / (msm.γ1 - msm.g - msm.γ2/msm.R_u)at 296 - which makes 665-667 and 678-682 the pair stated backwards. 760 is a plainer slip in the same derivation:{eq}`eq:stardynamics`writes \(y^*_{t+1} = \Lambda^t y^*_t\) where the recursion is \(y^*_{t+1} = \Lambda y^*_t\) (the \(\Lambda^t\) belongs in \(y^*_t = \Lambda^t y^*_0\)). And 455 writes \(b_0 = \gamma_1 - \gamma_0 R_0^{-1}\) using a parameter \(\gamma_0\) that does not exist in the model - 362 gives the same formula correctly as \(b_0 = \gamma_1 - \gamma_2/R_0\).[qe-writing-004] — Avoid unnecessary capitalization in narrative text. Count: 1. Lines: 1180. Example: mid-sentence ‘Method’.
[qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 3. Lines: 47, 476, 956. Example: three terms are bolded repeatedly instead of once at their definition. inflation tax is bold in the Overview at 47, before it is defined, and bold again in the recap at 955, while the actual definition - “the quantity \(1 - R_t\) can be interpreted as an inflation tax rate” - is at 473. Laffer curve is bold three times (48, 476, 956) and explained at 478-484. initial condition is bold at 395 and again at 421. The pattern is that the Overview and the recap bold the vocabulary they preview, so by the time the reader reaches the definition the bold has stopped signalling it. The rest of the file is disciplined - equilibrium (117), real balances (135), steady state and invariant (171-173), seigniorage (212), base (483), state and jump (795), incomplete (974) are each bolded exactly once at the point of definition, and italic is used only for genuine emphasis (dynamic 35, sequences 36, reduced / higher 50, ratios 177 and 194, determined 435, don’t 799, complete / selecting 982).
[qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 3. Lines: 330, 535, 905. Example: the two time-path figures cannot show what the lecture asks the reader to read off them.
draw_paths(523-550) anddraw_iterations(887-927) plot every path with**line_paramsand nolabel, and neither callsax.legend(), so{numref}`R0_path`renders ten curves and{numref}`p0_path`eight with nothing to identify them - even though 555 says “each line below shows a path associated with a different \(R_0\)” and 940 lists eight specific \(p_0\) values of which the first,p0_bar, is the unique non-perverse one the whole section exists to isolate. Second,{numref}`infl_tax`(313-336) is the figure 958 and 962 send the reader back to twice in order to see the two stationary rates and the perverse comparative static, and it marks neither: it draws the seigniorage curve and a horizontal line at \(g\) and stops, with no vertical lines at \(R_\ell\), \(R_u\) or \(\bar R_{\max}\) - althoughannotate_graph(516-521) already does exactly that job for the other two figures and could be reused. Third, the caption at 317 is a 32-word sentence describing the figure element by element (“solid blue curve”, “dotted red line”) and it is wrong about one of them: 330 draws the deficit line withlinestyle='--', which is dashed, not dotted.
Low severity#
None found.
Strengths#
The lecture computes the same object two independent ways and then proves the two agree. Method 1 indexes the continuum of equilibria by \(R_0\) (360-386), Method 2 by \(p_0\) through the eigendecomposition of \(H\) (574-861), 439 promises that the two selections are “intimately tied”, and
mi_ex3part c (1180-1181, 1219-1222) closes the loop by recovering \(\bar p_0\) from{eq}`eq:p0fromR0`- which is a far stronger check on a numerical method than any single implementation could be.The three
{prf:remark}blocks after Method 1 (388-406) isolate the three things a reader needs and would otherwise miss - that the method is indirect, that equilibria are not unique, and that the continuum is indexed by the choice of \(R_0\) from a stated interval - one sentence each, out of the flow of the derivation.The
{note}at 478-484 explains the Laffer curve from scratch (hump shape, two rates raising the same revenue, the base shrinking as the rate rises) before the term is asked to carry any weight, which is exactly the right move for an introductory series where the reader may be meeting it for the first time.The lecture states its own incompleteness and then acts on it: 972-993 declares the model incomplete because a continuum of paths satisfies every equilibrium condition, says precisely what has to be chosen to complete it, gives the plausibility ground for the choice, and names both where the choice is used (
{doc}`unpleasant`) and where it is defended differently ({doc}`laffer_adaptive`,{cite}`bruno1990seigniorage`).The state/jump distinction is introduced only after the algebra forces it: 787-796 shows that \(Q^{-1}y_0\) must have a zero component, observes that \(m_0\) is given so “\(p_0\) has to do all the adjusting”, and only then names the informal vocabulary - so jump variable arrives as a description of something the reader has just watched happen.
831-864 does not assert that the two formulas for \(p_0\) agree; it derives it, using \(Q^{-1}Q = I\) to turn \(-(Q^{22})^{-1}Q^{21}\) into \(Q_{21}Q_{11}^{-1}\) in four steps, and 866-869 then evaluates it.
The three exercises each check a different claim of the body numerically rather than drilling the code: the Laffer peak and the non-existence of a steady state beyond it (997-1078, with the discriminant computed explicitly at 1065), the comparative statics of both roots in \(g\) against analytically stated boundary conditions (1083-1153), and the quantity-theory proportionality of \(\bar p_0\) in \(m_0\) (1158-1230).
annotate_graph(516-521) is factored out once and reused on both multi-path figures, drawing \(R_u\), \(R_\ell\) and \(\gamma_2/\gamma_1\) as labelled reference lines placed outside the axes - the figures’ one real strength, and the reason the missing legends stand out.
Recommended actions#
The whole Math finding for this file is a false positive.
qe-math-001x2 at line 446 fires on\gamma_1and\gamma_0inside a MyST%comment block (443-447) that never reaches the published page. Do not wrap them in$...$; the right edit is to delete the dead block, which also removes theqe-writing-008hit at 443 (see scanner_doubts).Settle the eigenvalue ordering, which is currently contradictory and unguarded. 665-667 says \(\Lambda = \mathrm{diag}({R_\ell}^{-1}, {R_u}^{-1})\) and 678-679 reads
R_l = 1 / Λ[0], both of which need the larger eigenvalue first; 772-780 andp0_bar = (Q[1, 0]/Q[0, 0]) * msm.M0at 867 both need the smaller one first. Thep0slist at 940 and the Method 1 value at 296 show thatp0_baris meant to be the small number, so 665-667 and 678-682 are the pair to fix. Then sort explicitly -idx = np.argsort(Λ); Λ, Q = Λ[idx], Q[:, idx]- so nothing depends on LAPACK’s return order, here or in the three other places that assume one (279-281, 678-679, 867).Fix \(\gamma_0\) at 455: there is no \(\gamma_0\) in the model, and 362 states the same formula correctly as \(b_0 = \gamma_1 - \gamma_2/R_0\).
Correct
{eq}`eq:stardynamics`at 760: the recursion is \(y^*_{t+1} = \Lambda y^*_t\), not \(\Lambda^t y^*_t\) - the power belongs in the solution \(y^*_t = \Lambda^t y^*_0\), which is what 767 goes on to reason about.Give
{numref}`R0_path`and{numref}`p0_path`legends: passlabel=f'$R_0={R0:.3f}$'at 535 and 538 andlabel=f'$p_0={p0:.2f}$'at 905, 908 and 912, and label thep0_barpath distinctly - it is the one path in the figure that does not converge to the perverse steady state, and nothing currently marks it.Mark the two steady states on
{numref}`infl_tax`: 958 and 962 both send the reader to that figure to see \(R_\ell\), \(R_u\) and the perverse comparative static, and it shows only the curve and the deficit line.annotate_graph(516-521) already draws exactly those reference lines. Also correct the caption at 317, which says “dotted red line” where 330 drawslinestyle='--', and trim it from 32 words (qe-fig-004).Merge the duplicate method descriptions: “### Method 1” (360-386) and “## Computation method 1” (441-458) state the same algorithm with the same equations, as do “### Method 2” (408-439) and “## Computation method 2” (574-643). Keep the preview short or drop it, and delete the orphaned sentence at 352, whose computation was already done at 300-302.
Rename \(\overline R\) at 223-229 (say \(R^{\max} = 1\), or just write the interval as \([\gamma_2/\gamma_1, 1]\)): as written the upper endpoint of the interval and the variable ranging over it are both an overbarred \(R\).
Settle the \(Q\) index notation: \(Q^{ij}\) for an element of \(Q^{-1}\) (816-820) collides with
Q^{-1}_{11}for the reciprocal \(1/Q_{11}\) (854, 877, written the other way at 698 and 861), and \(Q^{\{2\}}\) (807) adds a third form for a row. Something like \(\tilde Q_{ij}\) for elements of \(Q^{-1}\) and \(1/Q_{11}\) spelled as a fraction would remove all three ambiguities.Rewrite the “in this lecture
{doc}`X`” construction at 18, 43, 52, 54, 62 and 968 - it reads as a pointer to the document the reader is already in - and cut the Overview/recap duplication at 47-48 versus 955-956 and 52 versus 968.Add the missing Part b. heading to the
mi_ex1solution: 1026 labels part a and 1059 labels part c, so the cell at 1038-1056 that answers part b looks like it belongs to part a.Fix the typos, several of which are in load-bearing sentences: “simulataneously” (129), “the same amounts real revenue” (340), “two countervailing courses” for forces (483), “or model is incomplete” for “our model” (974), “show indicate” and “rates of return on the price level” (958), “state-state” (352), “integar” (923).
Sweep the code and figures: move the import at 263 above 262, fix the continuation indents at 272-274 and 512-514, normalise operator spacing and remove the double space at 291, wrap the five over-long lines (1049, 1068, 1135, 1140, 1142), use raw strings for the LaTeX labels at 1048, 1051, 1126, 1211, 1212, add mystnb metadata to the three exercise figure cells (1038, 1108, 1203), set
lw=2on the ten line plots, drop the fourfigsize/dpioverrides (262, 328, 525, 889), and clear the 90 internal double spaces and the two-sentence paragraph at 410.