short_path#

  • Series: lecture-python-intro

  • File: lectures/short_path.md

  • Audit date: 2026-08-26

  • Corpus snapshot: a12d17c0ef

  • Categories audited: writing, math, code, figures, links, admonitions (JAX out of scope)

  • Overall score: 8.5 / 10

  • Priority: LOW

Score breakdown#

Category

Score

One-line note

Writing

5.5/10

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

Math

10/10

no mechanical violations detected.

Code

7.5/10

qe-code-001 ×5.

JAX

out of scope

JAX rules target lecture-jax.

Figures

8/10

qe-fig-005 ×4; qe-fig-002 ×4.

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: 231, 232, 388, 436. Example: the main loop cell carries three whitespace defects in six lines: blank lines containing only spaces at 231 and 234, and trailing whitespace after if np.array_equal(next_J, J): at 232. 436 writes return(J), parenthesising a return value as though it were a call, where the same function’s sibling at 419 returns cleanly. 388 leaves one blank line before a top-level def. Two things that a linter flags and that should be left alone: the seven E241 multiple spaces after ',' hits at 201-204 are the column alignment of the distance matrix literal, which is exactly the “closer to mathematical notation” exception this rule grants, and the capital Q and J for the matrix and the value function are likewise justified.

Medium severity#

  • [qe-fig-002] — Prefer code-generated figures. Count: 4. Lines: 57, 78, 84, 96. Example: static image .png.

  • [qe-fig-005] — Descriptive figure names for cross-referencing. Count: 4. Lines: 57, 78, 84, 96. Example: {figure} without :name:.

  • [qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 4. Lines: 34, 68, 171, 460. Example: the Overview and problem statement run three overlapping lists of applications: 30-34 (economics, operations research and transportation, robotics and AI, telecommunication routing, “etc., etc.”), 38-39 (Google Maps, routing packets on the internet), and 70-72 (minimum cost for a supplier, routing of packets on the internet, “etc., etc.”) - internet routing appears in all three, and two of the three end in the non-item “* etc., etc.” as a bullet. 171-172 (“Having an algorithm is a good start, but we also need to think about how to implement it on a computer”) restates 140-141 (“Let’s look at an algorithm for computing \(J\) and then think about how to implement it”) one section later. 460 is filler in a chatty register the rest of the lecture avoids: “Okay, now we have the necessary functions, let’s call them to do the job we were assigned.”.

  • [qe-writing-003] (reviewer) — Maintain logical flow. Count: 4. Lines: 76, 177, 210, 268. Example: the displayed definition of \(Q\) and the coded matrix disagree, and the disagreement is exactly what makes the algorithm terminate. 177-184 defines \(Q(v,w) = c(v,w)\) if \(w \in F_v\) and \(+\infty\) otherwise, with no exception for the diagonal; 210-213 then says “the cost of staying still (on the principle diagonal) is set to np.inf for non-destination nodes … 0 for the destination node”, and 207 duly sets Q[6,6] = 0. Under the definition at 177-184 that entry requires \(G \in F_G\) - a self-loop at the destination, which neither the figure at 57 nor the edge list shows - so the reader who takes the definition literally cannot reproduce the matrix, and the one entry that stops the iteration is the unexplained one. (“principle diagonal” at 210 should be “principal”.) Second, 74-86 breaks a two-item list across two figures: bullet “A, C, F, G at cost 8” (76), then a figure (78), then bullet “A, D, F, G at cost 8” (82), then a figure (84) - so each figure reads as a caption for the bullet below it rather than above it, and the list is not recognisable as a list. Third, the {note} at 266-269 tells the reader to “consider replacing np.equal() with np.allclose()”, and np.equal() appears nowhere in the lecture: the main-text loop uses np.array_equal (232) and the solution uses np.allclose (430). Fourth, 220-222 initialises the guess with np.zeros_like(nodes, dtype=int) where nodes is a range object, so the integer dtype that the exercise note at 266-268 warns about is imposed by an idiom the text never explains; np.zeros(7, dtype=int) is what is meant, and the inf entries of Q avoid an assignment error only because every node in this particular graph has a finite-cost outgoing edge.

  • [qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 3. Lines: 61, 66, 92. Example: four terms are defined and bolded correctly - dynamic programming (41), Bellman equation (131), successive approximations (148), distance matrix (186) - and the graph vocabulary the lecture depends on is glossed in bare parentheses instead: “node (vertex)” and “Arrows (edges)” at 61-63, and “weighted directed graphs” at 66, which is the name of the object under study and arrives inside a parenthetical aside. The most conspicuous omission is at 92: “Let \(J(v)\) denote the minimum cost-to-go from node \(v\)” introduces the cost-to-go function, the central object of the lecture and of the two sections named after it (138, 215), without bold - while the distance matrix, a helper, gets it. No italic appears anywhere in the lecture, so emphasis has no marker when one is wanted.

  • [qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 2. Lines: 57, 227. Example: the lecture is about graphs and every graph in it is a hand-drawn PNG that cannot be checked, regenerated or reused. The four {figure} directives at 57, 78, 84 and 96 point at /_static/lecture_specific/short_path/graph*.png, each with an empty body - no caption, no :name:, no :alt: - so the reader gets four unlabelled images and the drafted qe-fig-002 and qe-fig-005 counts of 4 each. The cost is not only mechanical: the distance matrix Q at 201-207 is the same 7-node graph in machine-readable form, and nothing draws one from the other, so a reader cannot verify that the matrix matches the picture, and the two “optimal path” figures at 78 and 84 highlight routes that no code produces. The 100-node exercise graph (271-373) is then given as 100 lines of text with no picture at all, so the reader has no idea what object the algorithm is being run on. A networkx drawing built from Q would replace all four PNGs, would extend to the exercise, and would let the highlighted paths be generated by print_best_path (446-457) rather than drawn by hand. Second, the section that matters most, “The algorithm” (143-167), is about a sequence \(J_0, J_1, J_2, \ldots\) converging to \(J\), and the code at 219-239 prints only the fixed point. For the 7-node graph the iteration takes a handful of steps and each \(J_n\) is seven small integers; printing or plotting the first four rows would make {eq}`spguess` and {eq}`spbell` concrete and would show the reader what “This sequence converges to \(J\)” (164) means.

Low severity#

None found.

Strengths#

  • The lecture builds the Bellman equation in the right order and says at each step what is known and what is not: assume \(J\) is known and finding the path is “almost trivial” (118), then “But how can we find the cost-to-go function \(J\)?” (120), then the self-consistency condition {eq}`spbell`, then “The Bellman equation can be thought of as a restriction that \(J\) must satisfy” and “What we want to do now is use this restriction to compute \(J\)” (133-136).

  • {eq}`spprebell` and {eq}`spbell` are deliberately the same expression, given first as the rule for choosing the next step and then as the equation \(J\) satisfies - which is the whole conceptual move of dynamic programming, made by writing one formula twice.

  • The example graph is small enough to solve by eye and the lecture does so first (74-86, two optimal paths at cost 8), so when the algorithm returns the same numbers at 238 the check at 241 is a real one: “This matches with the numbers we obtained by inspection above”.

  • The algorithm is stated as three numbered steps in mathematics (160-162) before any code, and the implementation section then names its two representational decisions explicitly - the cost function becomes a matrix \(Q\) (174-186), the sequence \(\{J_n\}\) becomes NumPy arrays (215) - so the translation from algorithm to program is visible rather than implicit.

  • The distance matrix literal at 201-207 is column-aligned so that the 7x7 structure is readable at a glance, and 210-213 then explains the one non-obvious design choice, the diagonal, in two labelled bullets.

  • The lecture is honest about what it does not prove: “Although we omit the proof, we’ll prove similar claims in our other lectures on dynamic programming” (166-167), rather than asserting convergence or burying it.

  • The exercise scales the same problem by more than an order of magnitude (7 nodes to 100) and supplies the data in the lecture itself via %%file graph.txt (272), so it runs with no external download, and the exercise text explains the file format from one example line (254-262).

  • The solution is factored the way the algorithm was described - a reader function, a Bellman operator, a fixed-point loop, a path extractor (388-457) - the Bellman operator is vectorised with a stated reason (415), the floating-point convergence test is changed to np.allclose with a stated reason (439-440), and the answer is then cross-checked against \(J[0]\) (468-471).