von_neumann_model#
Series: lecture-python.myst
File:
lectures/von_neumann_model.mdAudit date: 2026-08-26
Corpus snapshot:
e25fdf2345Categories 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 |
|
Math |
5/10 |
|
Code |
7.5/10 |
|
JAX |
out of scope |
JAX rules target |
Figures |
7/10 |
|
References |
8.5/10 |
|
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) whereif not dual:is meant. 242-243 printsres.messageon a failed solve and then falls straight through tores.x[-1]at 246, which raises onres.x is None. 268 and 304 both writefor iter in range(maxit):, shadowing the builtin. 279-285 and 314-320 assignxandponly inside theif abs(UB - LB) < tol:branch and thenreturn γ, x, punconditionally, so a run that exhaustsmaxitraisesUnboundLocalErrorinstead of reporting non-convergence. 943-946 indexes a numpy array throughrange([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 calln1.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 callN.bounds()twice per panel where each call runsfsolvetwice. 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 forNeumannitself (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\quadand\qquadare meant. And 1058 wraps a bare fraction in\left(\frac{1}{\alpha_0}\right)x_0^\topwhere nothing needs bracketing. The transpose also drifts across the subscript in adjacent lines:x_{t}^\top Aat 492 againstx^\top_{t}Bat 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
Neumannclass arrives at line 63 under one sentence, “The code below provides theNeumannclass”, 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;expansionandinterest(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
imshowof 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: onenp.linalg.eig(A1)against theα_0already 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
Neumannclass checks its own preconditions at construction (105-121) and reports them through__repr__/__str__(123-140), son1andn2at 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 -
expansionreports the \(p\) from the dual,interestreports 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.
Recommended actions#
Fix the algebra at 851:
M(\gamma)p'' = M(\gamma'') + (\gamma'' - \gamma)Ap''is missing a \(p''\) on the first right-hand term, so as written a matrix is added to a vector.Move the
Neumannclass out of line 63 to just before the algorithm section at 867, or split it:boundsafter 891,zerosumafter 770,expansion/interestafter 926. Every docstring in it forward-references material that appears six hundred lines later.Settle bold for definitions and italic for emphasis. Twelve definitions are currently italic (574, 576, 592, 594, 681, 706, 748, 749, 760, 761, 921, 1042) against twenty in bold, and 427 versus 1042 gets the same kind of term both ways. Put the four method names at 893, 925, 962 and 972 in backticks like
Neumannat 33, and undo the bold-around-code at 884 and 890.Give irreducibility a picture. The distinction between the two examples is a block structure in the matrices at 435-454, and nothing in the lecture shows it -
imshowof the four matrices, or the activity-good bipartite graph, plus the{irr}row already sketched at 138 and left commented out.Convert the 26
\mathbf{0}occurrences to plain \(0\) (qe-math-004), brace the unicodeβat 38 as$\beta$to match$\alpha$in the same sentence (qe-math-001), and lower-case the seven Title Case H2 headings at 362, 472, 509, 687, 739, 989 and 1035.Harden the class:
if not dualat 206,res.statuschecked beforeres.xis read at 242-246,x/pinitialised before the bisection loops at 268 and 304 so non-convergence reports rather than raisingUnboundLocalError,iterrenamed, and the two assert messages at 106-109 rewritten without the backslash continuation that embeds fourteen spaces in the text the user sees.Compute the special case at 1035-1080: check
np.linalg.eig(A1)against theα_0from 966 and against \(x_0\), and give the section a closing paragraph - the file currently ends on a display at line 1080 with no conclusion.Sweep the remaining items:
{cite}for the author-year citations in prose at 69, 690, 729, 1016 (and fix “Kemeny et al. (1967)” at 1016, which should be 1956), the 15 multi-sentence paragraphs (354, 574, 641, 646, 670, 696, 783, 789, 846, 905 and five more), the five double spaces (554, 610, 651, 982, 1066), the redundant re-solves at 281-282, 316-317, 932-934 and 956-957, and one spelling for \(\gamma^*\).