newton_method#

  • Series: lecture-python.myst

  • File: lectures/newton_method.md

  • Audit date: 2026-08-26

  • Corpus snapshot: e25fdf2345

  • Categories 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

qe-writing-001 ×3; qe-writing-005 ×4; qe-writing-003 ×4, +3 more.

Math

9.5/10

qe-math-009 ×4.

Code

7/10

qe-code-001 ×6; qe-code-004 ×9.

JAX

out of scope

JAX rules target lecture-jax.

Figures

6/10

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

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 with x_0=0.8 on 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) and k**α (962) against ((s * A) / δ) ** (1 / (1 - α)) (170), k ** - 1) (323), p_0 ** (-1 / 2) (730) and p_1 ** (-1 / 2) (733) - the rule names a**b explicitly, so the four spaced ones are the deviations. 792 uses the Python builtin any on a JAX array (if any(jnp.isnan(y))) where jnp.any is meant; it happens to work by iterating the array. 954 names a list initLs (also 969, 1077), which is neither PEP8’s init_ls nor a mathematical symbol. And 968-974 hand-rolls a counter (attempt = 1attempt += 1) for the same job that 1085 does correctly with enumerate(initLs, start=1), in the same lecture. Quoting is split roughly evenly between ' and " throughout, most visibly in the adjacent print('-'*64) (973) and print("-" * 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 A changes 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 normaliser s = jnp.sum(A, axis=0) - which is not a savings rate and sits fourteen lines from the exercise where s = 0.2 is. (4) The same iteration is indexed \(t\) in {eq}`newtons_method` (304) and {eq}`oneD-newton` (416) and \(n\) in {eq}`multi-newton` (763), while n is separately the series length in compute_iterates (237) and the iteration counter in newton (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. e is jnp.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 writes j_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 sets dim = 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’s root function” - but after the two %%time cells 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 optimized scipy function”). (4) 667-671 draws both goods’ zero contours on one axes by calling plot_excess_demand twice, 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 %%time cells and two scalar error prints; the figure it needs is runtime, or iteration count, against dim for newton versus root, 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, although plot_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_45 is 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, and multivariate_solow(k) - k at 984 - five separate residual checks, one after each solve.

  • newton fails loudly instead of returning garbage: 438-439 raises on max_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 NamedTuple with a factory (145-157), so g (163), Dg (321), exact_fixed_point (168) and q (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_law at 160 and 957, newtons_method at 326, oneD-newton at 420 and 766, motivation and newton_method2 in the derivation they belong to - and (first_newton_attempt) (422) is used at 774 to point back at the one-dimensional implementation being generalised.