mix_model#

  • Series: lecture-python.myst

  • File: lectures/mix_model.md

  • Audit date: 2026-08-26

  • Corpus snapshot: e25fdf2345

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

5/10

qe-writing-003 ×5; qe-writing-005 ×3; qe-writing-002 ×4, +1 more.

Math

9/10

qe-math-009 ×3; qe-math-015 (proposed) ×1.

Code

7/10

qe-code-001 ×5; qe-code-003 ×1.

JAX

out of scope

JAX rules target lecture-jax.

Figures

7/10

qe-fig-006 ×2; qe-fig-005 ×3.

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: 240, 330, 552, 719, 866. Example: flake8 finds five items and every one is real. 330 is the sharpest: def update(π, l) uses l as a parameter name (E741), which PEP8 names explicitly as forbidden because it is indistinguishable from 1 in most fonts - and the file already writes the same object as l_t, l_arr, l_seq elsewhere (175, 188, 428, 571). 552 and 558 assign lambdas (E731, h = lambda x: α * f(x) + (1 - α) * g(x), twice in adjacent functions where one module-level def h(x, α) would serve both). 719 assigns y_samp and never uses it (F841) and 866 assigns T = w.size and never uses it (F841). The remaining item a linter cannot see is a shadowing that matters: p is the Beta density function at 157 and is called as such at 162-163, and then draw_lottery(key, p, N) (240) and draw_lottery_MC(key, p, N) (258) bind p to the mixing probability, so inside those two functions the name means something unrelated and the density is unreachable - the call sites pass α (281-282, 416, 712, 855, 951), which is what the parameter should be called. Minor: 159 leaves a-1 and b-1 unspaced inside the exponents (E226), which is defensible as tracking \(x^{a-1}(1-x)^{b-1}\), and 751 loops for i in range(len(sizes)) where enumerate is meant.

  • [qe-writing-003] (reviewer) — Maintain logical flow. Count: 5. Lines: 14, 56, 278, 817, 396. Example: (1) Line 14 anchors this lecture as (likelihood-ratio-process)=, which is the subject of a different lecture in the same series: likelihood_ratio_process.md:14 carries (likelihood_ratio_process)=, so the two targets differ only in hyphen versus underscore, and anyone writing [](likelihood-ratio-process) lands on a lecture titled “Incorrect Models”. The anchor is referenced nowhere in the corpus (I grepped all five series) and the lecture is linked as {doc}`mix_model` from likelihood_bayes.md:618 and divergence_measures.md:560, so it is a copy-paste leftover. (2) \(\pi_t\) is defined twice with two different operators: 56 writes \(\pi_t = \mathbb{E}[\textrm{nature chose distribution } f \mid w^t]\) and 308 writes \(\pi_t = \mathbb{P}\{q = f \mid w^t\}\) - the second is right, and the first puts an event inside an expectation. (3) The section that promises to “generate samples using each of them and verify that they match well” (203) verifies at α = 0.0 (278), where the mixture is entirely \(G\) and nothing is being compounded at all; the density curve plotted at 289 reduces to \(g\), so the figure demonstrates the two samplers on the one value of \(\alpha\) at which the compound lottery is not compound. (4) The exercise (807-835) renames the mixing probability from \(\alpha\) to \(x\) without saying so - 817 speaks of “the uncertainty about \(x\)” as if \(x\) had been introduced - while \(x\) is the argument of the densities everywhere else in the file (def p(x, a, b) at 157, lambda x: at 162-163, 552, 558). (5) The H2 at 396, “What a type 1 agent learns when mixture \(H\) generates data”, uses the CDF where the next line and the rest of the lecture use \(h\) (398, 92, 111, 497, 780).

Medium severity#

  • [qe-code-003] — Package installation at lecture top. Count: 1. Lines: 20. Example: non-Anaconda import with no install cell: [‘jax’].

  • [qe-fig-005] — Descriptive figure names for cross-referencing. Count: 3. Lines: 123, 913, 942. Example: code-cell figure without mystnb figure metadata.

  • [qe-fig-006] — Lowercase axis labels. Count: 2. Lines: 933, 962. Example: axis label Posterior mean of $x$.

  • [qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 3. Lines: 398, 516, 502. Example: three notational shortcuts that cost the reader more than they save. (1) 398 writes the parameterised mixture as “\(h;\alpha\)” - a bare semicolon inside math with no delimiters - where 497 and 677 write the same object properly as \(h(w \mid \alpha)\), and 413 repeats the h;α form in a docstring. (2) 516 writes the limit condition as \(\min_{f,g}\{KL_g, KL_f\}\): the subscript makes \(f\) and \(g\) look like minimisation variables when the minimum is over the two displayed values, and the next two lines (518-520) state the condition correctly and without notation (“\(\pi_t\) goes to one if and only if \(KL_f < KL_g\)”), so the display can simply go. (3) \(KL_g(\alpha)\) and \(KL_f(\alpha)\) (502-508) subscript the divergence with its second argument, so \(KL_g\) is the divergence of \(h\) from \(g\); the code then names the same quantities KL_g, KL_f, KL_g_v, KL_f_v, KL_g_quad, KL_f_quad, KL_g_arr, KL_f_arr and labels the figure KL(h, g) (601), which is the clearer form and could be the notation throughout.

  • [qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 4. Lines: 74, 802, 234, 660. Example: the overview says everything three times. 74-111 introduces the two agents (78-79 “Our first type of agent erroneously thinks that at time \(-1\), nature once and for all chose \(f\) or \(g\)”; 81-82 “Our second type of agent knows, correctly, that nature mixes \(f\) and \(g\)”) and then immediately re-introduces both at greater length (92-101 for type 1, 103-111 for type 2); the concluding remarks then give the same two paragraphs a third time (773-788). Cutting 92-111 would lose nothing. On sentence length, 802 is 55 words in a single clause chain, 660-661 is 39 words split across a colon, and 96 and 98-99 are 30 and 38 words. And 234 cites a Google search query as a reference - [Mr. P Solver video on Monte Carlo simulation](https://www.google.com/search?q=Mr.+P+Solver+video+on+Monte+Carlo+simulation&oq=...) - which is not a link to anything stable and carries a tracking-style oq parameter; either link the video or drop the sentence.

  • [qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 3. Lines: 32, 65, 797. Example: the same term is italicised where it is defined and bolded where it is used again: 32 writes “A compound lottery can be said to create a mixture distribution” and 68 writes “nature perpetually draws from the mixture distribution with CDF”, 36 lines apart. 65 uses bold for pure emphasis - “Now, each period \(t \geq 0\), nature flips a possibly unfair coin” - where the file’s own convention is italic (34 timing, 89 correct, 96 true, 99 closest, 115 mix, 798 inverting). And the italic markers are split between two spellings, _..._ at 32 and 797 and *...* at 34, 89, 96, 99 and 115, with 797-798 using bold and underscore-italic in one sentence (“solve an inverse problem of somehow inverting \(s(x \mid \theta)\)”).

  • [qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 3. Lines: 146, 269, 790. Example: (1) The two distributions the whole lecture is about are never drawn. 146 says “Let’s use Python to generate two Beta distributions”, 152-163 sets \(F = \mathrm{Beta}(1,1)\) and \(G = \mathrm{Beta}(3,1.2)\) and defines f and g - and no cell ever plots them. Every later claim is about how close \(h = \alpha f + (1-\alpha) g\) is to each of them (497, 502-508, 663-665), and the reader cannot see either one; the only density that appears is at 289, and there \(\alpha = 0\) so it is just \(g\). Three lines added to the existing xs = np.linspace(0, 1, 1000) cell would fix it. (2) The sampler check at 269-292 draws one histogram pair at \(\alpha = 0\); a small panel over \(\alpha \in \{0, 0.5, 1\}\) would both verify the samplers where it matters and show the mixture deforming from \(g\) to \(f\), which is the mechanism behind the KL crossing at 610-614. (3) The closing generalization (790-802) - a manifold \(S\) of models \(s(X\mid\theta)\), nature’s \(h\) lying outside it, Bayes selecting the \(\theta\) that minimizes the KL divergence from \(h\) - is the canonical projection diagram, is the lecture’s actual thesis, and is given as two paragraphs of prose. It is also the one figure that would tie the two agents together, since type 2’s model contains \(h\) and type 1’s does not.

Low severity#

  • [qe-math-015 (proposed)] (reviewer) — Lowercase for densities/PMFs, uppercase for CDFs. Count: 1. Lines: 396. Example: the lecture is otherwise a model of this rule - \(F\), \(G\) and \(H\) are the CDFs and are used only as such (\(H(w) = \alpha F(w) + (1-\alpha)G(w)\) at 71, “if a random variable \(X\) has CDF \(F\)” at 223, jsp.beta.cdf at 262), while \(f\), \(g\) and \(h\) are the densities and are used only as such (“The two density functions” at 161, \(h(w) = \alpha f(w) + (1-\alpha)g(w)\) at 497, the integrands of the two divergences at 502-508) - which is exactly why the one slip is visible. The H2 at 396 reads “What a type 1 agent learns when mixture \(H\) generates data”, using the CDF letter for the data-generating object, and line 398 immediately below it uses the density: “We now study what happens when the mixture distribution \(h;\alpha\) truly generated the data each period”. Every other statement of that idea uses \(h\) (92-93, 111, 780, 800).

Strengths#

  • The two sampling methods are implemented independently and then checked against each other and against the analytic density in one figure (239-292): draw_lottery flips a coin and draws from the selected Beta, draw_lottery_MC inverts the mixture CDF with jnp.searchsorted, and both histograms are overlaid on α * f(xs) + (1 - α) * g(xs).

  • The KL result is not asserted but verified twice over. 588-607 plots \(KL_g(\alpha)\) and \(KL_f(\alpha)\); 612-614 locates the \(\alpha\) at which they cross; and then 624-657 runs 5000-period belief simulations across the whole \(\alpha\) grid and scatters the realized limit points on a second axis of the same figure, so the reader sees the limit flip from 0 to 1 exactly where the two divergence curves cross.

  • The KL integrals are computed two ways and both are kept: a JAX Riemann sum (523-546) and scipy.integrate.quad on the same integrand (550-564), which is the right kind of redundancy for a quantity the whole argument turns on.

  • The lecture is honest about what its misspecified learner achieves: 96-101 says the type 1 agent “muddles through and eventually learns something interesting and useful, even though it is not true”, 98-99 names the sense in which the limit is closest, and 660-665 then states the two cases in terms of the measured divergences rather than in words.

  • The belief figure at 441-462 puts the posterior paths and the log likelihood ratio on twinned axes with two different initial conditions, which is what makes 475-476 and 491 readable - one panel carries the belief dynamics, the driving process and the dependence on \(\pi_0\) at once.

  • The Bayes-law recursion is derived rather than quoted: 342-389 works from the one-step update to \(1/\pi_{t+1} - 1 = (1/\ell)(1/\pi_t - 1)\), iterates it to {eq}`eq:bayeslaw103`, and pauses at 379-381 to check that \(\pi_{t+1}\) stays in \((0,1)\) - and both labelled equations are then referenced where they are used (324, 337, 391, 393).

  • The type 2 agent’s posterior is shown collapsing rather than described: 708-737 builds the numpyro Mixture likelihood with a uniform prior on \(\alpha\), and 742-766 overlays the MCMC posterior at six history lengths from 5 to 25,000 observations in one panel, against a true \(\alpha = 0.8\).

  • Randomness is threaded explicitly through JAX keys everywhere - jax.random.split at 185, 243, 280, 632, 946 - so every figure in the lecture is reproducible from jax.random.key(42), and the exercise’s twenty paths (943-953) come from one split rather than a re-seeded generator.

  • The exercise solution answers the question in two stages and reports both: three different priors converging to the same \(x = 0.5\) over 200 periods (847-936), then twenty independent paths over 10,000 periods from a uniform prior (942-966), which is the right way to show that the result is about the data rather than the prior.

  • Five of the seven figure cells carry mystnb caption and name metadata (269-275, 465-471, 481-487, 588-594, 624-630, 742-748), which is why qe-fig-005 reports only the import cell and the two exercise-solution cells.