theil_2#
Series: lecture-python.myst
File:
lectures/theil_2.mdAudit date: 2026-08-26
Corpus snapshot:
e25fdf2345Categories audited: writing, math, code, figures, references, links, admonitions (JAX out of scope)
Overall score: 8.1 / 10
Priority: LOW
Score breakdown#
Category |
Score |
One-line note |
|---|---|---|
Writing |
4.5/10 |
|
Math |
7.5/10 |
|
Code |
6.5/10 |
|
JAX |
out of scope |
JAX rules target |
Figures |
8.5/10 |
|
References |
10/10 |
no mechanical violations detected. |
Links |
10/10 |
no mechanical violations detected. |
Admonitions |
10/10 |
no mechanical violations detected. |
Issues#
Critical#
None found.
High severity#
[qe-code-002] — Use Unicode symbols for Greek letters in code. Count: 8. Lines: 622, 632, 657, 658, 663, 668, 706, 707. Example: spelled-out
beta.[qe-fig-001] — Do not set figure size unless necessary. Count: 5. Lines: 163, 341, 660, 709, 779. Example: figsize=.
Medium severity#
[qe-code-001] (reviewer) — Follow PEP8 unless closer to mathematical notation. Count: 4. Lines: 574, 631, 532, 663. Example: the cell that is supposed to verify Hall’s martingale computes two things and throws both away. 573 builds the closed-form policy
F_pi = [[(R-1), (R-1)/(R-ρ)]]from 561 and 574 buildsA_cl_std = A_pi - B_pi @ F_pi;A_cl_stdis never read again, soF_piexists only to feed a dead binding, and the closed form of 561 is never checked againstLQ’s ownF. What the cell does print isφ_std = 1.0 / (β_hat * R_rate)(576) whereR_rate = 1.0 / β_hat(530), so the printed value is exactly 1 by construction rather than by computation. Three more bindings are dead:d_std(328),K_rob(337, rebound and used only at 840-858), andθ_pi(534), which is defined with a comment explaining the mapping and never used - the exercise recomputesθ_val = -1.0 / sat 838. Separately, 631 writesν_rob ** 2with spaces around the exponentiation operator, which the rule singles out to be written closed up (theil_1:361 has the same); six assignments pad with extra spaces before=to align columns, which PEP8 discourages (532, 534, 573, 626, 631, 632); 626-627 uses a backslash continuation inside an expression that parentheses would wrap; and three code lines run past 79 characters (332 at 89, 663 at 98, 669 at 80).[qe-math-004] — Do not use bold face for matrices or vectors. Count: 3. Lines: 288, 289, 296. Example: \mathbf.
[qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 4. Lines: 117, 508, 135, 447. Example: the state-cost and control-cost matrices are named one way here and the opposite way in the companion lecture, and neither matches the library both lectures call. 117 writes the return as \(r(y,u) = -y^\top Q y - u^\top R u\), so \(Q\) is the state cost and \(R\) the control cost, and the code agrees - 150-151 comment
Q_matas “state cost” andR_matas “control cost”.quantecon.LQ, whose docstring gives the objective as \(x_t' R x_t + u_t' Q u_t\), uses the reverse: \(R\) on the state, \(Q\) on the control - and that is the namingtheil_1adopts at its lines 277-278 (R_mat= state cost,Q_mat= control cost). Both lectures then compensate in the call,theil_1withLQ(Q_mat, R_mat, A, B, ...)andtheil_2withLQ(R_mat, Q_mat, A, B, ...), so both compute the same thing - I ran both namings and got P = 1.472317, F = 0.524797, d = 27.974028 either way - but a reader moving between two lectures that share a title, a model and a code cell meets the same two matrices under swapped names, with no warning in either file. Second, \(R\) then does triple duty inside this lecture: control-cost matrix (117, 151), gross return on savings (494, 502, 561), and at 508 the collision is patched with a subscript rather than a rename - \(R_{\text{ctrl}} = 1\), a symbol that occurs exactly once - while the code carriesR_mat(151),R_rate(530) andR_pi(543). Third, \(\sigma\) is the risk-sensitivity parameter, introduced at 58-59 with \(\theta = -\sigma^{-1}\) and used at 438-468 and 682-684, and also the noise level of the recap figure at 135, 153, 166, 171 and 324. Fourth, \(\rho\) is the scalar term of the candidate continuation value at 447 (\(U_{t+1}^e = -y_{t+1}^\top \Omega y_{t+1} - \rho\), with \(\hat\rho\) at 456-460) and the endowment persistence at 499-502 and 531.[qe-writing-001] — Use one sentence per paragraph. Count: 3. Lines: 50, 291, 758. Example: 2 sentences in one paragraph.
[qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 3. Lines: 505, 49, 758. Example: 505-508 is a 50-word sentence whose main verb arrives at word 45: “After absorbing the constants \(-b\) and \(\mu_d(1-\rho)\) by augmenting the state vector, or equivalently by working with deviations from steady state, setting \(w_{t+1} \equiv 0\) and taking \(Q = 0\) (return depends only on the control \(\mu_{ct}\)) and \(R_{\text{ctrl}} = 1\) puts this in the standard LQ form” - four separate modelling steps, two of them parenthesised, before the reader learns what they accomplish. 49-52 is 45 words carrying a question, an answer and a mechanism in one block (and is the drafted
qe-writing-001finding at 50). 758-761 states the exercise in 45 words across two sentences in one paragraph, with the plotting instruction tacked on after the claim to be verified.[qe-writing-003] (reviewer) — Maintain logical flow. Count: 3. Lines: 612, 618, 96. Example: the observational-equivalence formula is used 76 lines before it is stated. 612-614 says “The observational equivalence formula
{eq}`eq:oe_locus`(derived below) immediately gives the robust AR(1) coefficient”, 621-628 definesbeta_tildeimplementing it, and 631-635 evaluates it - while{eq}`eq:oe_locus`itself appears at 688-693 and \(\alpha^2 = \tilde\nu^2\) is defined at 695, afterα_sq = ν_rob ** 2has already been written at 631. The lecture flags the inversion, but the code that depends on the formula still runs first. Second, \(\tilde\nu\) is an input read out of a paper rather than a computed quantity: 616-619 says “The innovation scale \(\tilde\nu\) follows from the robust permanent income formula with the distorted persistence;{cite:t}`HST_1999`report \(\tilde\nu \approx 8.0473\)”, no code follows the formula, and 630 hard-codesν_rob = 8.0473. Everything downstream rests on it - the robust simulated path at 658,α_sqat 631, the entire locus plotted at 705-721 - so the lecture’s central quantitative claim is anchored to a number it does not derive. Third, the pair’s cross-references use three different mechanisms in two files: 38 links back with a bare markdown link[this lecture on certainty equivalence](theil_1), 96 uses{ref}companion lecture <certainty_equiv_theil1>``, andtheil_1:391 links forward with[certainty equivalence and model uncertainty](theil_2); the exercise labels diverge the same way,theil1_ex1in one file againstce_ex2andce_ex3here - a numbering that starts at 2 with noce_ex1in either lecture.[qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 4. Lines: 38, 54, 557, 724. Example: the lecture’s title concept is italicised where it is coined - 38, “established an important certainty equivalence (CE) property” - which is exactly what the companion lecture does at its line 51, and
theil_1then bolds the same term at its line 174, so across the pair the term appears in both styles and this file never bolds it. 54 italicises Risk-sensitive preferences. as a list header and 416 bolds risk-sensitive preferences as a definition, for the same term. 557-558 sets a whole clause in bold - “the marginal utility of consumption is a martingale” - and the term inside it, marginal utility of consumption, was already bolded at 488-489, so the bold is doing emphasis on top of a definition it repeats. Whole-sentence italics appear twice more, at 366-368 (“both the policy gain and the value matrix depend on the robustness parameter \(\theta\) and the noise-loading matrix \(C\)”) and 724-726 (“activating a preference for robustness is observationally equivalent – for consumption and saving behaviour – to increasing the discount factor”), where the lecture’s own word-level italics (distorted 51, conceals 232, ordinary 260, same 279, does 312, fully trusts 418, not 729) are the correct form.[qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 2. Lines: 389, 413. Example: the distortion operator is the mechanism the lecture is about and it is never shown. \(\mathcal{D}\) appears at 385, 389, 456, 459 and 732, and
{eq}`eq:distortion_op`at 389 is literally \(\mathcal{D}(P) = \mathcal{D}(P;\, f_2,\, \theta)\) - an equation that restates its own argument list - with{eq}`eq:constant_p`at 396 doing the same for \(p\). Meanwhilequantecon.RBLQ.robust_rule()already returns the worst-case distortion matrix: it is bound toK_robat 337 in the very figure cell that plots \(F(\theta)\) and \(P(\theta)\), and discarded. A third panel showing \(K(\theta)\) next to those two would put the object the whole lecture turns on into a picture, and would show it going to zero as \(\theta \to \infty\) alongside the two quantities that already do. Second, the risk-sensitive section (413-468) has no figure at all: its content is that \(\mathcal{R}_t\) collapses to \(\mathbb{E}\) at \(\sigma = 0\) (441) and penalises right-tail risk for \(\sigma < 0\) (443-444), and one plot of \(\mathcal{R}_t(U)\) against \(\sigma\) for a Gaussian \(U\) - or of the exponential tilt \(\exp(\sigma U / 2)\) against \(U\) for two values of \(\sigma\) - would make the operator concrete. As written the section’s central claim sits in a blockquote at 467-468 and is not checked until exercisece_ex3, 330 lines later.[qe-writing-008] — Remove excessive whitespace between words. Count: 4. Lines: 50, 292, 467, 760. Example: 2 spaces.
Low severity#
[qe-fig-005] — Descriptive figure names for cross-referencing. Count: 1. Lines: 770. Example: code-cell figure without mystnb figure metadata.
Strengths#
The summary table at 739-743 answers the same three questions - does the policy depend on noise, which forecasts are used, does CE survive - for all three settings in one place, and it is the only spot in the lecture where the three are directly comparable.
The invariant half of the two-step algorithm is named explicitly every time the setting changes, so the reader can see what the extension does not touch: 309-310 (“step 1 is identical to the non-robust case; only step 2 changes, using distorted rather than rational forecasts”), 597-600 (“where \(h_1\) – the first step of the CE algorithm – is identical to the non-robust case. Only the expectations operator changes”), and again at 745-749.
Exercise
ce_ex3(799-872) does the work that the “Key equivalence” blockquote at 467-468 only asserts, and does it properly: for three values of \(\sigma\) it solves the robust problem withRBLQ, extracts \(\varphi\) from the closed loop \(A - BF\), inverts to \(\tilde\beta = 1/(\varphi R)\), re-solves a non-robust problem at that \(\tilde\beta\), and prints \(\max|F_{\text{rob}} - F_{\text{nr}}|\) - so observational equivalence is demonstrated rather than quoted.That same solution then says what the equivalence does not cover, and why: 868-872 points out that the worst-case distortion \(K\) has no analogue in the non-robust model, which is why the two parametrisations imply different asset prices - and 728-732 makes the same point in the main text, attributing it to the \(\mathcal{D}(P)\) matrix in the stochastic discount factor.
Two numerical choices are declared rather than smuggled in: 521-523 explains the
1e-8 Iregularisation and names the case that needs it (\(\beta R = 1\)), and the comment at 332 says why the robustness figure restricts \(\theta\) to \([2, 30]\) (“restrict attention to a numerically stable range”).Both limiting cases of the risk-sensitive operator are stated where the operator is defined: \(\sigma = 0\) recovers ordinary expectation by L’Hopital’s rule (441), and \(\sigma < 0\) penalises right-tail risk in the continuation value (443-444) - so the reader can locate the standard case inside the generalisation.
The robustness figure at 317-359 plots both objects that ordinary CE holds fixed - the policy gain \(F\) and the value matrix \(P\) - against \(\theta\) on one row, with the non-robust values drawn as horizontal reference lines and their numerical values in the legend labels, which makes the convergence claim at 362-364 checkable by eye.
Recommended actions#
Settle the \(Q\)/\(R\) naming across the pair. This lecture’s 117 and 150-151 put \(Q\) on the state and \(R\) on the control;
quantecon.LQ(whose docstring gives \(x'Rx + u'Qu\)) andtheil_1:277-278 do the reverse. Both call sites compensate in the argument order so the numbers agree, which is precisely what makes the disagreement hard to spot - align both lectures on the library’s convention, or add a note like the onephillips_escaping_nash:93-100 uses to warn about its own reversed ordering.Rename two of the three \(R\)’s: control-cost matrix (117, 151), gross return (494, 502, 561), and the once-used patch \(R_{\text{ctrl}}\) at 508. Do the same for \(\sigma\), which is the risk-sensitivity parameter at 58 and the noise level at 135, and for \(\rho\), which is a value-function constant at 447 and the endowment persistence at 499.
Either compute \(\tilde\nu\) from the robust permanent income formula that 616-617 gestures at, or say plainly that 8.0473 is taken from
{cite:t}`HST_1999`as an input. As written,ν_rob = 8.0473at 630 is the anchor forα_sq(631), the robust path (658) and the whole observational-equivalence locus (705-721).Move
{eq}`eq:oe_locus`(688-693) and the definition of \(\alpha^2\) (695) above their first use at 612-635, sobeta_tildeis not implementing a formula the reader has not yet seen.Delete the dead bindings:
A_cl_std(574) and with itF_pi(573), whose only consumer it is;d_std(328);θ_pi(534); and the unusedK_robat 337 - or better, useK_robin the figure as suggested above and useF_pito check the closed-form policy of 561 againstLQ’s own solution.Note for contributors: the
ax.set_titleat 783 is not aqe-fig-003violation. The rule’s exceptions clause allows embedded titles insidesolution-start/solution-endregions, and the checker honours it - which is why the drafted report shows noqe-fig-003findings for this lecture despite that call.Deduplicate the recap: the figure cell at 144-175 reproduces
theil_1:271-303 almost line for line - samea, b_coeff,q, r,β,σ_vals, sameset_ylim(0, 2 * max(F_vals) + 0.1)- with only the matrix names swapped and the titles removed. Either shorten it to the part the robustness section needs or point at the companion figure.Sweep the remaining items: drop the three
\mathbf{z}_tbold vectors (288, 289, 296) forqe-math-004; drop the fivefigsizeoverrides (163, 341, 660, 709, 779); addmystnb: figure: caption/namemetadata to the exercise figure at 770; split the three two-sentence paragraphs (50, 291, 758) and close the four double spaces (50, 292, 467, 760); writeν_rob**2at 631; remove the column-alignment padding before=at 532, 534, 573, 626, 631, 632; and convert the escaped-backslash label strings (166, 171, 343, 346, 663, 669, 711) to raw strings.