supply_demand_multiple_goods#
Series: lecture-python-intro
File:
lectures/supply_demand_multiple_goods.mdAudit date: 2026-08-26
Corpus snapshot:
a12d17c0efCategories audited: writing, math, code, figures, links, admonitions (JAX out of scope)
Overall score: 8.0 / 10
Priority: LOW
Score breakdown#
Category |
Score |
One-line note |
|---|---|---|
Writing |
4.5/10 |
|
Math |
9.5/10 |
|
Code |
6.5/10 |
|
JAX |
out of scope |
JAX rules target |
Figures |
7.5/10 |
|
References |
N/A |
no citations in this lecture. |
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: 228, 266, 740, 753, 1031. Example: twenty lines carry trailing whitespace, and most of them are indentation-only blank lines inside class bodies - 228, 251, 260 in
ExchangeEconomy, 656, 675 inProductionEconomy, 973, 984 inMonopoly- plus the per-parameter signature lines at 229-231, 657-661 and 974-978. Five lambdas are assigned to names where PEP8 asks for adef:supply_invanddemand_inv(740-741),marg_cost,marg_revanddemand_inv(1030-1032). Ten lines exceed 79 characters, four of them past 110: 1047 at 116, 1046 at 113, 753 and 754 at 112. 1031 multiplies by a redundant literal,-2 * 1 / μ * Π * Π * x. And 266, 690 and 999 call the built-inany()on a NumPy array (if any(c < 0),if any(Π @ c - b >= 0)) rather thannp.any(), which works only because the arrays are one-dimensional and raises on anything else - in a lecture whose whole subject is the \(n\)-good generalisation. One stray item: 1027 is the comment# computewith nothing after it.[qe-fig-008] — Use lw=2 for line charts. Count: 5. Lines: 750, 751, 1042, 1043, 1044. Example: plot() without lw=.
[qe-writing-008] — Remove excessive whitespace between words. Count: 10. Lines: 52, 54, 144, 219, 355, 448, 934, 1117. Example: 2 spaces.
Medium severity#
[qe-code-002] — Use Unicode symbols for Greek letters in code. Count: 3. Lines: 368, 371, 373. Example: spelled-out
beta.[qe-fig-006] — Lowercase axis labels. Count: 4. Lines: 763, 764, 1060, 1061. Example: axis label
Quantity.[qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 3. Lines: 91, 360, 1126. Example: 1126 and 1132 write the area under the inverse supply curve as
h c + \frac{1}{2} c^\top J c, but \(h\) and \(c\) are both \(n \times 1\) (566, 99), so \(hc\) is not defined - the same quantity is written correctly as \(h^\top q\) at 551 and \(h^\top q\) at 912, and the first-order condition at 1141 differentiates as though the transpose were there. The transpose has simply been dropped from the welfare criterion, twice, in the section that states the first welfare theorem. 91-93 introduces \(\Pi\) as \(m \times n\) and \(b\) as \(m \times 1\) and then never uses \(m\) again: every formula from 106 onward, and every instance in the code, has \(\Pi\) square, andExchangeEconomyinverts it outright at 263 - so the extra dimension is notation the lecture carries and never needs. And 360 defines two symbols in one chained assignment,(1+r) := R := \frac{p_1}{p_2}, of which neither \(R\) nor \(r\) is used anywhere else in the lecture.[qe-writing-001] — Use one sentence per paragraph. Count: 1. Lines: 482. Example: 2 sentences in one paragraph.
[qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 3. Lines: 35, 292, 843. Example: 35-47 spends thirteen lines on two bulleted lists of eight economists, each a bare Wikipedia link with no accompanying claim about what any of them contributed - the two welfare theorems at 52-54 do the work those names were introduced for. The Marshallian/Hicksian distinction is set up four separate times: named at 144, explained in a
{note}at 177-183, explained again as a two-bullet contrast at 287-290, and then explained a third time at 292-302 under “contemplate different mental experiments”, after which 304 says it will be discussed “more below” and it is not. And 843-847 is a section heading whose entire body is two bullets in the future tense (“we’ll do some experiments like those above”, plus the stale TODO), followed by four code cells with no narration - contrast the single-good example at 768-840, which states what it will do in four bullets and then says what each experiment showed (“this raises both the equilibrium price and quantity”, 840). Smaller edits: 390 is ungrammatical (“our consumer confronts risk means in particular that”), 442 switches to “his/her” where 134 and 275 use “he” and “his”, and 646 refers to something “described above in the main text” while itself being the main text.[qe-writing-003] (reviewer) — Maintain logical flow. Count: 4. Lines: 426, 618, 847, 967. Example: 426-430 labels the wrong vector: “A consumer’s endowment vector is \(c = \begin{bmatrix} c_1 \cr c_2 \end{bmatrix}\)” - that is the consumption vector, and the endowment \(e\) was already displayed at 417 four lines earlier. 618 says “this equation is the counterpart of equilibrium quantity
{eq}`eq:old1`for the scalar \(n=1\) model with which we began”, but this lecture does not begin with a scalar model -{doc}`intro_supply_demand`does, and that is whereeq:old1is defined (its line 653). The reference resolves, because{eq}labels are project-wide, but it silently lands the reader in a different lecture while the sentence claims the model is this one’s. 847 ships a literal(TODO)in published prose - “we can do experiments with a diagonal \(\Pi\) and also with a non-diagonal \(\Pi\) matrix to study how cross-slopes affect responses of \(p\) and \(c\) to various shifts in \(b\) (TODO)” - and it is stale, since the four cells immediately below at 849-895 do precisely that. 967 puts the conclusion 145 lines before its evidence: “the plot indicates that the monopolist sets output lower than the competitive equilibrium quantity”, whereplot_monopolyis defined at 1010 and first called at 1112. Two smaller ones in the same family: the{note}at 177-183 cites{eq}`eq:old2p`a hundred lines before it is written at 281, and 304 promises “we’ll discuss these distinct demand curves more below”, which never happens.[qe-writing-004] — Avoid unnecessary capitalization in narrative text. Count: 1. Lines: 642. Example: mid-sentence ‘Economy’.
[qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 4. Lines: 275, 289, 578, 904. Example: bold carries two jobs at once throughout, so it stops distinguishing either. Definitions are bold, correctly and often: marginal utility of wealth (152), numeraire (283), compensated (302), expected utility (398), Arrow securities (440), insurance (444), marginal costs (566), inverse supply curve (585), consumer surplus and producer surplus (649-650), price-taker (899). But so is plain emphasis: only (275), static (386), marginal revenue to marginal costs (578), price equals marginal revenue (583) - a whole clause - demand curve and price in the contrast at 904, diagonal and non-diagonal (847), substitution and income (294, 298), lower output (923). Meanwhile italic appears exactly once in 1154 lines, at 313, and there it marks part of a definition (known probability distribution), so the lecture has no working emphasis form at all. Three terms are also re-bolded rather than bolded once: Marshallian at 144, 182 and 289, Hicksian at 182 and 290, risk at 313, 388 and 390.
[qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 3. Lines: 849, 1046, 1112. Example: a lecture titled “Supply and Demand with Many Goods” draws two figures and both are of the single-good case (
plot_competitive_equilibriumcalled at 796, 816 and 837;plot_monopolycalled at 1112). Every multi-good result reaches the reader as printed vectors: the endowment economy (377-381), the dynamic interpretation (367-382), the risk economy (454-469), the three comparative statics ofsdm_ex3(496-532), the two-good production economy (849-895) and the multi-good monopoly comparison (1069-1090). The worst case is 843-895, whose stated purpose at 847 is “to study how cross-slopes affect responses of \(p\) and \(c\) to various shifts in \(b\)”: it runs four experiments - diagonal \(\Pi\), shifted \(b\), non-diagonal \(\Pi\), shifted \(b\) again - and delivers eight numbers with no table, no figure and not one sentence of interpretation, so the cross-slope effect the section exists to demonstrate is left for the reader to compute by eye from four separate cell outputs. Second, the monopoly figure (1010-1062) shades consumer and producer surplus at the monopoly quantity but not the deadweight-loss wedge between \(q\) and \(c\), which is exactly what 923-927 and 967-969 claim - that the monopolist sets output below both the planner’s and the competitive quantity. Third, both figures hardcode five hex colours (750-758, 1042-1055) instead of the matplotlib cycle, and the two surplus fills are#EED1CFand#E6E6F5, a pale-pink/pale-lavender pair with very little contrast between them and against white - for the figure’s principal content.
Low severity#
None found.
Strengths#
The three linear-algebra identities the lecture will actually use are stated up front, once, with their dimensions declared (65-85), and then every derivative in the file is a direct application of one of them - \(\partial L/\partial c\) at 159-160, \(\partial C(q)/\partial q\) at 569, \(\partial p^\top q/\partial q\) at 580, and the first-order condition of the planning problem at 1141. A reader who takes the three formulas on faith can follow the whole lecture.
The quadratic-utility device is used to make three apparently different economies the same object, and each mapping is written out rather than asserted: dynamics by setting \(\Pi = \mathrm{diag}(1, \sqrt\beta)\) and \(b = (b_1, \sqrt\beta b_2)\) (330-343), risk by setting \(\Pi = \mathrm{diag}(\sqrt\lambda, \sqrt{1-\lambda})\) (412-423), and each is then instantiated in the same
ExchangeEconomyclass (377, 464) so the reader sees one implementation serve both.The dynamic reinterpretation is carried all the way to its economic content: 345-363 shows the budget constraint becoming a present-value identity, names both sides, gives the units of the relative price (“time \(2\) goods per unit of time \(1\) goods”), and only then identifies \(p_1/p_2\) as the gross interest rate - which is the step that makes the trick more than an algebraic coincidence.
The Marshallian/Hicksian distinction is drawn in terms of what is held fixed and what is solved for, with the mental experiment spelled out for each (287-302), including the observation that a Hicksian curve leaves \(p^\top e + w\) unaltered “because we freeze \(\mu\) and solve for \(w\)” - a genuinely clarifying sentence that most treatments at this level omit.
ExchangeEconomyandProductionEconomyboth check their own maintained assumption and fail loudly rather than returning nonsense: the non-satiation threshold at 245-246 (“set bliss points further away”), the negative-allocation check at 266-268 that prints the offending allocation before raising, and the same non-satiation check at 690-691 and 999-1000 - which matters here because{eq}`eq:bversusc`at 131 is exactly the condition that makes equilibrium prices positive.MonopolysubclassesProductionEconomy(972-983) so that the monopoly and competitive outcomes are computed from one set of parameters by one object, which is why the comparison at 1082-1089 and the two-equilibrium figure at 1049-1055 are guaranteed to be like-for-like.The lecture closes the loop it opened: 52-54 states the two welfare theorems, and 1115-1148 poses the planning problem, differentiates it, and shows the first-order condition implies
{eq}`eq:old5p`- the competitive equilibrium quantity derived 500 lines earlier - concluding “this is another version of the first welfare theorem”.
Recommended actions#
Restore the transposes in the welfare criterion at 1126 and 1132:
h cshould be \(h^\top c\), as 551 and 912 write it, and as the first-order condition at 1141 assumes. As printed, the objective the first welfare theorem is proved against is dimensionally ill-formed.Fix the label at 426-430: the displayed vector is consumption, not endowment - \(e\) was given at 417 - and the sentence currently gives the reader two different definitions of the endowment vector thirteen lines apart.
Delete the
(TODO)at 847 and narrate the four cells at 849-895: their stated purpose is to show how cross-slopes in \(\Pi\) change the response of \(p\) and \(c\) to a shift in \(b\), and the section currently prints eight numbers and says nothing about them. A small table, or two lines of the shape “with \(\Pi\) diagonal the price of good 1 rises by X; with the off-diagonal 0.5 it rises by Y”, is what the section promises.Move the sentence at 967 (“the plot indicates that the monopolist sets output lower…”) next to the plot it describes, which is defined at 1010 and drawn at 1112 - and shade the deadweight-loss region between \(q\) and \(c\) in
plot_monopoly, since that wedge is the entire content of 923-927.Attribute the reference at 618:
eq:old1lives in{doc}`intro_supply_demand`, and “the scalar \(n=1\) model with which we began” is that lecture’s model, not this one’s. Say so, or restate the scalar result here. Also move the{eq}`eq:old2p`citation in the note at 180 after 281, where the equation is defined.Give at least one multi-good result a picture. The lecture is about \(n\) goods and every figure is \(n = 1\); candidates that need no new machinery are a two-panel version of the surplus figure for the two-good economy at 849-895, and a bar comparison of the competitive and monopoly price and quantity vectors from 1082-1089.
Split bold from italic: keep bold for the many correct definitions and move the emphasis to italic - only (275), static (386), marginal revenue to marginal costs (578), price equals marginal revenue (583, a whole clause in bold), demand curve / price (904), diagonal / non-diagonal (847), substitution / income (294, 298), lower output (923). Bold Marshallian once (144, not also 182 and 289), Hicksian once, risk once.
Cut the Marshallian/Hicksian material from four passes to one: 144, the note at 177-183, the bullets at 287-290 and the paragraph at 292-302 all say the same thing, and 304’s promise of more discussion below is not kept.
Weaken the implicit assumption in
ExchangeEconomy.competitive_equilibrium: 263 computesΠ_inv = inv(Π)and 264 uses it, which requires \(\Pi\) square and nonsingular, whereas the model assumes only that \(\Pi\) has linearly independent columns so that \(\Pi^\top \Pi\) is invertible (106-108). Using{eq}`eq:old3`directly -c = slope_dc @ (Π.T @ b - p)- matches the stated assumption and the displayed formula.Clean the code: the twenty trailing-whitespace lines (228-231, 251, 260, 656-661, 675, 973-978, 984), the five named lambdas (740, 741, 1030, 1031, 1032), the ten over-long lines (1047 at 116 characters, 1046 at 113, 753 and 754 at 112, plus 701, 725, 758, 988, 1051, 1055), the redundant
2 * 1 / μat 1031, the empty# computecomment at 1027, and the three built-inany()calls on arrays (266, 690, 999) which should benp.any.Reconcile
Hbetween the plotting helpers and the classes:competitive_equilibriumandequilibrium_with_monopolyuseH = .5 * (J + J.T)(681, 991) whilecompute_surplusand both plot functions useH = J(702, 733, 1020) - identical for the 1x1 case they are called with today, and wrong the moment either is used with a non-symmetric two-goodJlike the one at 857.Sweep the mechanical items: the Title Case H2 at 273 (“Digression: Marshallian and Hicksian demand curves” - note “Marshallian” and “Hicksian” are proper adjectives and correct, so only the rest of the heading is at issue), the three spelled-out
betaidentifiers at 368, 371 and 373 which should beβ(qe-code-002), the four Title Case axis labels at 763-764 and 1060-1061 (qe-fig-006),lw=2on the five line plots (750, 751, 1042, 1043, 1044), the ten internal double spaces, the two-sentence paragraph at 482, and the ungrammatical 390 and “his/her” at 442.