newton_method#
Series: lecture-python.myst
File:
lectures/newton_method.mdAudit date: 2026-08-26
Corpus snapshot:
e25fdf2345Categories audited: writing, math, code, figures, links, admonitions (JAX out of scope)
Overall score: 7.8 / 10
Priority: HIGH
Score breakdown#
Category |
Score |
One-line note |
|---|---|---|
Writing |
4/10 |
|
Math |
9.5/10 |
|
Code |
7/10 |
|
JAX |
out of scope |
JAX rules target |
Figures |
6/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: 6. Lines: 170, 468, 730, 792, 954, 968. Example: flake8 finds only three items in the file, all the same one: E251 at 468 (
newton(f = lambda x: g(x, params) - x, x_0=0.8)), 743 (jac = lambda p: jacobian_e(p, A, b, c)) and 863 (jac = lambda p: jax.jacobian(e)(p, A, b, c)) - spaces around a keyword-argument equals, and inconsistent withx_0=0.8on the same line. The rest is what a linter cannot see. Exponentiation is spaced in four places and tight in two, in the same file:k**α(165) andk**α(962) against((s * A) / δ) ** (1 / (1 - α))(170),k ** (α - 1)(323),p_0 ** (-1 / 2)(730) andp_1 ** (-1 / 2)(733) - the rule namesa**bexplicitly, so the four spaced ones are the deviations. 792 uses the Python builtinanyon a JAX array (if any(jnp.isnan(y))) wherejnp.anyis meant; it happens to work by iterating the array. 954 names a listinitLs(also 969, 1077), which is neither PEP8’sinit_lsnor a mathematical symbol. And 968-974 hand-rolls a counter (attempt = 1…attempt += 1) for the same job that 1085 does correctly withenumerate(initLs, start=1), in the same lecture. Quoting is split roughly evenly between'and"throughout, most visibly in the adjacentprint('-'*64)(973) andprint("-" * 64)(1088).[qe-code-004] — Use quantecon Timer context manager. Count: 9. Lines: 690, 739, 804, 847, 859, 971, 1002, 1010, 1087. Example: %%time.
[qe-fig-005] — Descriptive figure names for cross-referencing. Count: 6. Lines: 210, 217, 247, 647, 655, 667. Example: code-cell figure without mystnb figure metadata.
[qe-fig-008] — Use lw=2 for line charts. Count: 6. Lines: 255, 348, 351, 354, 357, 360. Example: plot() without lw=.
[qe-writing-006] — Capitalize lecture titles properly. Count: 1. Lines: 378. Example: H2 Title Case: ‘Root-Finding in one dimension’ (Root-Finding).
Medium severity#
[qe-fig-001] — Do not set figure size unless necessary. Count: 4. Lines: 212, 219, 344, 668. Example: figsize=.
[qe-fig-003] — No matplotlib embedded titles. Count: 1. Lines: 641. Example: .set_title.
[qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 4. Lines: 116, 326, 763, 832. Example: the notation is otherwise plain - no calligraphic, blackboard or bold anywhere, and \(g\), \(f\), \(k\), \(p\), \(e\), \(J\) are all single plain letters - so the cost is three letters carrying two jobs each. (1) \(A\) is the Solow productivity parameter (116, 122, 137, 170) and the matrix of demand cross-coefficients (528, 572, 589, 831, 884, 1032); in the exercise-1 solution the same name is the \(3\times3\) Solow matrix (948) and in the exercise-2 solution the \(3\times3\) demand matrix (1073), so
Achanges meaning four times in the file. (2) \(q\) is the Newton fixed-point map \(q(k)\) (326, 329, 431, 782) and the supply and demand quantities \(q^s_i(p)\), \(q^d_i(p)\) (502, 508); both are written as \(q(\cdot)\). (3) \(s\) is the savings rate (116, 123, 890, 950, 994) and, at 832, the column-sum normalisers = jnp.sum(A, axis=0)- which is not a savings rate and sits fourteen lines from the exercise wheres = 0.2is. (4) The same iteration is indexed \(t\) in{eq}`newtons_method`(304) and{eq}`oneD-newton`(416) and \(n\) in{eq}`multi-newton`(763), whilenis separately the series length incompute_iterates(237) and the iteration counter innewton(435, 786).[qe-writing-001] — Use one sentence per paragraph. Count: 3. Lines: 513, 606, 701. Example: 2 sentences in one paragraph.
[qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 4. Lines: 66, 450, 513, 977. Example: the lecture states its own plan four times before doing anything: the two bullets at 66-72 differ only in the words “fixed point” and “zero”, 74-82 gives the same programme again in prose, 100-105 repeats it for the first section, and 481-488 repeats it for the second. 450-452 is a whole paragraph in parentheses that says “Newton’s method” three times in one sentence (“That said, when we want to apply Newton’s method using techniques such as automatic differentiation or GPU acceleration, it will be helpful to know how to implement Newton’s method ourselves”). 513 packs two sentences into one paragraph and 44 words into the pair. And 977, “the results are invariant to the starting values given the well-defined property of this question”, does not say what property is meant; the sentence it is trying to write is at 979 (“the number of iterations it takes to converge is dependent on the starting values”). Minor: 224 says “We see that \(k^*\) is indeed the unique positive fixed point” from two 45-degree plots, which show a crossing, not uniqueness.
[qe-writing-003] (reviewer) — Maintain logical flow. Count: 4. Lines: 726, 485, 856, 667. Example: (1) The hand-coded Jacobian at 726-735 does not differentiate the excess demand function the lecture defines.
eisjnp.exp(-A @ p) + c - b * jnp.sqrt(p)(565), so \(e_0\) depends on \(p_1\) through the exponent \(-(a_{00}p_0 + a_{01}p_1)\); but 730 writesj_00 = -a_00 * jnp.exp(-a_00 * p_0) - (b[0] / 2) * p_0 ** (-1 / 2), dropping \(a_{01}p_1\) from the exponent, and 731-733 drop the other term the same way in all three remaining entries. So the display at 717-723 and the code below it disagree, and 748’s claim that “the solution is even more accurate” rests on a Jacobian that is wrong away from \(p_1 = 0\). (2) 485 promises “a larger market with 5,000 goods”; the section that arrives setsdim = 3000(825) and 820-822 say 3,000 three times. (3) 488 promises “a significant performance gain when using Newton’s method” and 856 says “we compare the runtime and accuracy of Newton’s method to SciPy’srootfunction” - but after the two%%timecells at 847 and 859 the section simply ends and no comparison is ever stated, while the only prose verdict in the lecture points the other way (815: “the speed is not better than the optimizedscipyfunction”). (4) 667-671 draws both goods’ zero contours on one axes by callingplot_excess_demandtwice, and that function sets an axes title (641); the second call overwrites the first, so the combined figure is titled “Excess demand for good 1” although it shows both.[qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 4. Lines: 47, 131, 385, 515. Example: the file contains no bold and no italic at all - a grep for
**...**returns zero and every single-asterisk match is$k^*$- so every definition is marked some other way or not at all. Quotation marks do the job at 47 ((also called "roots")) and 385 (this "root-finding" problem); a hyperlink does it at 131 (Such a $k^*$ is called a [steady state](https://en.wikipedia.org/wiki/Steady_state)) and 44-47 (fixed points,zeros); and the rest are simply unmarked - “The excess demand function is” (515), “An equilibrium price vector \(p^*\)” (522), “the first-order approximation” (283), “successive approximation” (227-231), “the Jacobian” (713). Bolding the term at the point of definition would also give the reader something to search for, since several of these are used dozens of lines later (excess demand at 550, 560, 616, 663; successive approximation at 348, 351).[qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 3. Lines: 818, 276, 1082. Example: the low-dimensional half of the lecture is well drawn - two 45-degree diagrams, a time series, a two-panel trajectory comparison, three contour plots - and then the figures stop exactly where the lecture says the interesting part is. (1) §“A high-dimensional problem” (818-873) is the climax promised at 81-82 (“we leverage the power of automatic differentiation … to solve a very high-dimensional equilibrium problem”) and its entire output is four
%%timecells and two scalar error prints; the figure it needs is runtime, or iteration count, againstdimfornewtonversusroot, which would settle the performance claim at 488 in one panel instead of leaving the reader to compare two timing outputs by eye. (2) §“Newton’s method” (276-375) describes the construction geometrically - “update by solving for the fixed point of a tangent line at \(x_0\)” (280) - and never draws the tangent line, althoughplot_45(176-206) is already there to draw it on; the reference example the rule points to does exactly this for a comparable one-step construction. (3) Exercise 2’s conclusion, “Newton’s method may fail for some starting values” (1091), is demonstrated only by an exception traceback (1082-1089); the contour machinery at 621-643 would draw the basin of attraction over the same \((p_0, p_1)\) grid and show which starting values fail.
Low severity#
None found.
Strengths#
The same fixed point is computed four ways and each is checked against the one before: the closed form \(k^* = (sA/\delta)^{1/(1-\alpha)}\) (137, coded at 168-170), successive approximation over 10,000 steps (263-265), Newton’s fixed-point iteration (329-330), and finally Newton as a zero-finder on \(f(x) = g(x) - x\) (468) - so 475’s claim that five iterations suffice is made against a value the reader has already seen three times.
plot_45is called twice with different parameterisations (211-214, 218-221) rather than once, so the reader sees that the shape of the argument does not depend on the calibration.The trajectory figure is built to make exactly one comparison:
plot_trajectories(336-368) puts successive approximation and Newton steps on the same axes from two different initial conditions, and 363-364 labels the y-axis with \(k^*\) itself (ax.set_yticks((k_star,)),set_yticklabels(("$k^*$",))) so convergence is read off the tick rather than the number.Every solution in the lecture is verified by substitution rather than asserted:
jnp.max(jnp.abs(e(p, A, b, c)))at 704, 752, 809, 852 and 871, andmultivariate_solow(k) - kat 984 - five separate residual checks, one after each solve.newtonfails loudly instead of returning garbage: 438-439 raises onmax_iter, 792-793 raises when the step produces a NaN, and the exercise-2 cell is tagged:tags: [raises-exception](1083) so the failure is part of the published output - which is what turns 1091 (“Newton’s method may fail for some starting values”) into a demonstration rather than a claim.Parameters live in a
NamedTuplewith a factory (145-157), sog(163),Dg(321),exact_fixed_point(168) andq(329) all unpack the same object with the same line,A, s, α, δ = params, and a re-parameterisation is one call.The contour figures are constructed to answer the question being asked: 638 draws only the zero level set, 635-636 shades the surface with a colorbar in the single-good panels, and 667-671 then overlays both goods’ zero contours so that the crossing named at 674 (\(p \approx (1.6, 1.5)\)) is something the reader locates on the figure before the root finder confirms it at 697.
The exercise-1 hint (912-935) is genuinely useful rather than decorative: it hands the reader a diagonal \(A\) whose answer is the scalar formula already derived at 137, and links to the cell that printed it via the
(solved_k)anchor at 270, so a reader can self-check the multivariate code before trusting it on the real problem.Every labelled equation is referenced by a
[](label)link where it is used -motion_lawat 160 and 957,newtons_methodat 326,oneD-newtonat 420 and 766,motivationandnewton_method2in the derivation they belong to - and(first_newton_attempt)(422) is used at 774 to point back at the one-dimensional implementation being generalised.
Recommended actions#
Fix
jacobian_e(726-735). All four entries differentiate \(\exp(-a_{ij}p_j)\) instead of \(\exp(-(a_{i0}p_0 + a_{i1}p_1))\):j_00should be-a_00 * jnp.exp(-(a_00 * p_0 + a_01 * p_1)) - (b[0] / 2) * p_0**(-1 / 2)and similarly forj_01,j_10,j_11. Simplest correct version isjax.jacobian(e)- which 863 already uses - kept alongside the hand-written one so the display at 717-723 can be checked against it.Reconcile 485 (“5,000 goods”) with 820-825 (3,000), and finish the comparison the section sets up: after the timing cells at 847 and 859, say which method won and by how much, since 488 promises “a significant performance gain” and 815 is the only verdict in the file and says the opposite.
Move the axes title out of
plot_excess_demand(641) into the figure captions. This is the single qe-fig-003 hit and it is also a bug: 667-671 calls the function twice on one axes, so the two-contour figure ends up titled “Excess demand for good 1”.Add
mystnbcaption and name metadata to the six figure cells qe-fig-005 reports (210, 217, 247, 647, 655, 667), and to the trajectory cell at 370 while there; then drop the four hand-setfigsizevalues (212, 219, 344, 668) and addlw=2to the eight line plots (189, 254, 255, 348, 351, 354, 357, 360).Bold the definitions. There is no bold anywhere in the file, so “roots” (47), “steady state” (131), “root-finding” (385), “excess demand function” (515) and “equilibrium price vector” (522) currently rely on quotation marks, hyperlinks or nothing at all.
Draw the two figures the argument is missing: a tangent-line construction for §“Newton’s method” (276-306, using
plot_45), and runtime or iteration count againstdimfor §“A high-dimensional problem” (818-873). A basin-of-attraction map over the exercise-2 grid would also turn 1091 into something visible.Convert the nine
%%time/%timemagics (690, 739, 804, 847, 859, 971, 1002, 1010, 1087) to thequanteconTimercontext manager (qe-code-004) - that also removes the backslash continuation inside the magic at 971-972, which is the only place in the lecture where a statement is split with\.Rename to break the collisions: \(A\) (Solow productivity at 116 against the demand matrix at 528), \(q\) (the Newton map at 326 against supply and demand at 502-508) and
sat 832 (column-sum normaliser, against the savings rate everywhere else). Also settle the iteration index, which is \(t\) at 304 and 416 and \(n\) at 763.Code: drop the spaces around the keyword equals at 468, 743 and 863; write
a**btight at 170, 323, 730 and 733 to match 165 and 962; usejnp.anyat 792; renameinitLs(954, 969, 1077); and replace the manualattemptcounter at 968-974 withenumerate(initLs, start=1)as at 1085.Split the two two-sentence paragraphs qe-writing-001 reports (513, 701), cut three of the four statements of the lecture plan (66-72, 74-82, 100-105, 481-488), and rewrite 977, which currently explains the result by “the well-defined property of this question”.