von_neumann_model#

  • Series: lecture-python.myst

  • File: lectures/von_neumann_model.md

  • Audit date: 2026-08-26

  • Corpus snapshot: e25fdf2345

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

  • Overall score: 7.3 / 10

  • Priority: HIGH

Score breakdown#

Category

Score

One-line note

Writing

3/10

qe-writing-006 ×7; qe-writing-001 ×15; qe-writing-005 ×12, +5 more.

Math

5/10

qe-math-004 ×26; qe-math-001 ×1; qe-math-009 ×4.

Code

7.5/10

qe-code-001 ×6.

JAX

out of scope

JAX rules target lecture-jax.

Figures

7/10

qe-fig-003 ×2; qe-fig-005 ×1; qe-fig-008 ×1, +1 more.

References

8.5/10

qe-ref-001 ×2.

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: 6. Lines: 106, 206, 242, 268, 279, 943. Example: 106-109 continues two assert messages with a backslash and indents the continuation fourteen spaces, so the message a user actually sees is “The input and output matrices must have the same dimensions!” with the indentation embedded in the string. 206 tests if dual == False: (E712) where if not dual: is meant. 242-243 prints res.message on a failed solve and then falls straight through to res.x[-1] at 246, which raises on res.x is None. 268 and 304 both write for iter in range(maxit):, shadowing the builtin. 279-285 and 314-320 assign x and p only inside the if abs(UB - LB) < tol: branch and then return γ, x, p unconditionally, so a run that exhausts maxit raises UnboundLocalError instead of reporting non-convergence. 943-946 indexes a numpy array through range ([n1.zerosum(γ=γ_grid[i])[0] for i in range(numb_grid)]) with continuations one column short of the opening bracket (E128), and the same solve is repeated wastefully throughout: 932 and 934 call n1.zerosum(γ=γ) twice to print the two halves of one return value, 281-282 and 316-317 re-solve both LPs after the bisection has already solved them, and 956-957 call N.bounds() twice per panel where each call runs fsolve twice. Smaller items: the lambdas bound to names at 159-160 (E731), the dead commented-out {irr} row at 138, the trailing space inside the __str__ template at 134 that prints as a trailing space, and the blank first line of the code cell at 64.

  • [qe-math-004] — Do not use bold face for matrices or vectors. Count: 26. Lines: 327, 331, 333, 336, 337, 340, 341, 380, 387, 701, …. Example: \mathbf.

  • [qe-writing-001] — Use one sentence per paragraph. Count: 15. Lines: 354, 574, 641, 646, 670, 696, 783, 789, 846, 905, …. Example: 2 sentences in one paragraph.

  • [qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 12. Lines: 574, 576, 592, 594, 681, 706, 748, 760, 893, 921, …. Example: the file assigns the two markers inconsistently and the two systems sit side by side. Bold is used correctly for twenty-odd definitions - positive (330), non-negative (333), semi-positive (336), activities (367), goods (368), input matrix (369), output matrix (371), intensity (391), productive (400), cost/revenue (407), irreducibility (412), independent subset (418), irreducible (427), balanced growth (516), interest factor (529), value (715) - and then twelve equally definitional terms arrive in italic: technological expansion rate (574), optimal intensity vector (576), economic expansion rate (592), optimal price vector (594), economic solutions (681), solution (706, inside a {prf:definition} that is defining it), max-min problem / primal (748-749), min-max problem / dual (760-761), Existence (921) and simple (1042, again inside the {prf:definition} that defines it). 427 bolds irreducible and 1042 italicises simple - two definitions of the same kind, two markers, in the same lecture. Separately, four Python method names are set in italic where the file uses backticks for Neumann itself (33, 61, 457): the bounds method (893), the zerosum method (925), the expansion method (962), the interest method (972). 884 and 890 double up, bolding an inline code span: **``UB``**.

  • [qe-writing-006] — Capitalize lecture titles properly. Count: 7. Lines: 362, 472, 509, 687, 739, 989, 1035. Example: H2 Title Case: ‘Model Ingredients and Assumptions’ (Ingredients, Assumptions).

  • [qe-writing-008] — Remove excessive whitespace between words. Count: 5. Lines: 554, 610, 651, 982, 1066. Example: 2 spaces.

Medium severity#

  • [qe-fig-003] — No matplotlib embedded titles. Count: 2. Lines: 949, 954. Example: .suptitle.

  • [qe-math-001] — Prefer UTF-8 unicode for simple parameter mentions, be consistent. Count: 1. Lines: 38. Example: unicode β inside a math environment.

  • [qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 4. Lines: 513, 625, 736, 1058. Example: 513-519 introduces an operator to use once: “Let \(./\) denote an elementwise division of one vector by another”, then writes \(x_{t+1}./x_t = \alpha\) - and 522 immediately restates it as \(x_{t+1} = \alpha x_t\), which is the whole content and needs no new notation. One symbol has three spellings: \gamma^{* } with a space inside the braces at 625, 626, 627, 638, 642-644 and 647, \gamma^{*} at 618, 651, 660 and 689, and \gamma^* at 829, 982, 1011, 1018 and 1032. Manual spacing is used instead of the standard macros in more than twenty displays - \hspace{5mm} at 380 and 387, \hspace{1cm} at 478, 499, 526, 540, 1057 and 1079, \hspace{2mm} at 564-565, 585-586, 736 and 745-746 - with the amounts chosen ad hoc where \quad and \qquad are meant. And 1058 wraps a bare fraction in \left(\frac{1}{\alpha_0}\right)x_0^\top where nothing needs bracketing. The transpose also drifts across the subscript in adjacent lines: x_{t}^\top A at 492 against x^\top_{t}B at 493, 499 and 526.

  • [qe-ref-001] — Use correct citation style. Count: 2. Lines: 859. Example: {cite} in narrative flow: ‘. {cite}’.

  • [qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 4. Lines: 41, 596, 658, 1016. Example: 41-44 spends 34 ornamental words getting to a bullet list: “In addition to watching how the towering mind of John von Neumann formulated an equilibrium model of price and quantity vectors in balanced growth, this lecture shows how fruitfully to employ the following important tools”. 596-599 is 34 words that also fails to agree with itself and misnames its own subject: “Because the criterion functions in the technological expansion problem and the economical expansion problem are both linearly homogeneous, the optimality of \(x_0\) and \(p_0\) are defined only up to a positive scale factor” - “the optimality … are”, and “economical” for “economic”, which 579 gets right. 658-662 is 48 words joined by a semicolon carrying two separate complementary-slackness statements. 1016-1022 is 60 words defining finitely many economic solutions, the condition they satisfy, and the sub-economy interpretation, all in one sentence. 653-656 also doubles its verb (“We have already encountered and discussed the first two inequalities”).

  • [qe-writing-003] (reviewer) — Maintain logical flow. Count: 4. Lines: 61, 851, 1016, 1073. Example: the 258-line Neumann class arrives at line 63 under one sentence, “The code below provides the Neumann class”, six hundred lines before any of the material its methods implement. Every docstring forward-references what has not been written yet: bounds (150-154) cites “the proof of Theorem 9.8 in Gale (1960)” for bounds that are derived at 873-891; zerosum (168-201) writes out the primal and dual linear programs that are derived at 739-770; expansion and interest (252-264, 287-300) cite the Hamburger-Thompson-Weil bisection introduced at 869. Second, the proof at 837-854 is half inside its directive and half outside: {prf:proof} closes at 840, and the argument then continues as loose body text through two displays and two “hence” sentences to 854 - and line 851 has an error, M(\gamma)p'' = M(\gamma'') + (\gamma'' - \gamma)Ap'', where the first right-hand term must be \(M(\gamma'')p''\); without the \(p''\) a matrix is added to a vector. Third, 1016 attributes the finiteness result to “Kemeny et al. (1967)” where 35 and 675 cite the same authors as 1956 (kemeny1956generalization) and 1967 is Hamburger-Thompson-Weil’s year - and the surrounding prose cites Nash (729), Hamburger-Thompson-Weil (690), Gale (69, 153) and Kemeny et al. (69) by author and year with no {cite} at all, while the same works are properly cited elsewhere (772, 859, 869, 1024). Fourth, the lecture stops mid-argument: 1078-1080 is a displayed formula, and 1080 is the last line of the file - no closing sentence, no summary, no exercise, and nothing computed for the special case set up at 1035.

  • [qe-writing-004] — Avoid unnecessary capitalization in narrative text. Count: 4. Lines: 733, 985, 992, 1024. Example: mid-sentence ‘Theorem’.

  • [qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 3. Lines: 435, 899, 1035. Example: the lecture’s organising contrast is Example 1 against Example 2 - irreducible with \(\alpha_0 = \beta_0\) against reducible with \(\beta_0 < \alpha_0\) - and irreducibility is never shown, only defined (412-429) and asserted (434, 443, 985, 992). The two economies appear at 435-454 as raw zero-one literals, a \(3\times4\) pair and a \(5\times6\) pair, and the reader is left to find the independent subset by hand; a bipartite activity-good diagram, or imshow of the four matrices with the independent block outlined, would make the whole “Uniqueness and Irreducibility” section (989-1033) visible. The author evidently wanted this: line 138 is a commented-out # Irreducible : {irr} row in __str__, so the class prints the two assumption flags at 461-469 and stays silent on the property that decides the answer. Second, the bisection of 899-923 is described in two nested bullet lists and then only its answer is printed (966-979, 996-1006), while the figure at 939-959 already plots \(V(M(\gamma))\) against \(\gamma\) with both bounds marked - drawing the successive brackets on that curve would show the algorithm working on the object it is working on. Third, “A Special Case” (1035-1080) derives that \(1/\alpha_0\) is an eigenvalue of \(A\) with \(x_0\) the left eigenvector, and then never checks it: one np.linalg.eig(A1) against the α_0 already computed at 966 would close the loop, and the decomposition \(\alpha_0 = \max_i\{\alpha_i\}\) of 1078 is left as pure algebra.

Low severity#

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

  • [qe-fig-005] — Descriptive figure names for cross-referencing. Count: 1. Lines: 939. Example: code-cell figure without mystnb figure metadata.

  • [qe-fig-008] — Use lw=2 for line charts. Count: 1. Lines: 953. Example: plot() without lw=.

Strengths#

  • The notation section (323-360) is complete before it is needed and defines exactly the things that are non-standard here: the three vector orderings \(\gg\), \(\geq\) and \(>\) with positive, non-negative and semi-positive each given its own line (330-337), the extension to pairs of vectors (339-341), the column-vector convention and \(x^\top\) (343), \(\iota_n\) for a vector of ones (346-348), \(e^i\) for a unit vector (350-352), and the row/column slice notation \(a_{\cdot j}\), \(a_{i \cdot}\) (358-360) that the two assumptions at 380 and 387 then use.

  • The two assumptions are {prf:assumption} directives with labels (376-389) and are cross-referenced by {prf:ref} at 570, 591, 605, 608, 617, 634, 775, 786, 788 and 1015 - fifteen citations of two labels, so the reader never has to remember which assumption was which.

  • The duality argument is built in the right order and each inequality is given its economic reading before it is used: feasibility from the production timing convention (490-504), the no-profit condition from an outside investment opportunity earning \(\beta\) (529-545), then von Neumann’s theorem (613-630) whose third condition \(x_0^\top(B - \gamma^* A)p_0 = 0\) is unpacked at 658-662 into “if any good is oversupplied its price must be zero, and if any activity makes negative profit it must be unused”.

  • 604-608 states what the standard duality argument does and does not give - \(\beta_0 \leq \alpha_0\) follows from the two assumptions, \(\beta_0 \geq \alpha_0\) does not - which is exactly why von Neumann’s theorem is needed, and 670-685 then says what the theorem still does not give (uniqueness, and the exclusion of \(x_0^\top B p_0 = 0\)) and how Kemeny, Morgenstern and Thompson close that gap.

  • The zero-sum-game reformulation is developed from the general matrix game (696-737) through the primal and dual linear programs (739-770) to the restatement of the two assumptions as \(V(-A) < 0\) and \(V(B) > 0\) (772-793), so \(M(\gamma) = B - \gamma A\) at 799 arrives as the obvious object and the sign analysis at 805-829 follows mechanically.

  • The figure at 939-959 is the one figure the argument needs and it is built to be read: \(V(M(\gamma))\) on a 100-point grid for both examples on shared \(y\) axes, with a horizontal line at zero and the trivial lower and upper bounds drawn as vertical dashed lines - so the single root of Example 1 and the flat zero segment of Example 2 are visible side by side, and 1009-1012 refers back to it.

  • The Neumann class checks its own preconditions at construction (105-121) and reports them through __repr__/__str__ (123-140), so n1 and n2 at 461-469 print whether Assumption I and Assumption II hold rather than leaving the reader to trust that the hand-typed matrices satisfy them.

  • Both bisection methods are demonstrated on both economies (966-979, 996-1006) and each prints the dual object alongside the primal one - expansion reports the \(p\) from the dual, interest reports the \(x\) from the primal - which is what makes the difference between Example 1 and Example 2 legible in the printed output as well as in the figure.