short_path#
Series: lecture-python-intro
File:
lectures/short_path.mdAudit date: 2026-08-26
Corpus snapshot:
a12d17c0efCategories 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 |
|
Math |
10/10 |
no mechanical violations detected. |
Code |
7.5/10 |
|
JAX |
out of scope |
JAX rules target |
Figures |
8/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: 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 writesreturn(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 sevenE241 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 capitalQandJfor 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.inffor non-destination nodes … 0 for the destination node”, and 207 duly setsQ[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 replacingnp.equal()withnp.allclose()”, andnp.equal()appears nowhere in the lecture: the main-text loop usesnp.array_equal(232) and the solution usesnp.allclose(430). Fourth, 220-222 initialises the guess withnp.zeros_like(nodes, dtype=int)wherenodesis arangeobject, 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 theinfentries ofQavoid 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 matrixQat 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. Anetworkxdrawing built fromQwould replace all four PNGs, would extend to the exercise, and would let the highlighted paths be generated byprint_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.allclosewith a stated reason (439-440), and the answer is then cross-checked against \(J[0]\) (468-471).
Recommended actions#
Replace the four static PNGs (57, 78, 84, 96) with a
networkxdrawing generated fromQso the picture and the matrix at 201-207 cannot drift apart, and highlight the two optimal paths fromprint_best_pathoutput rather than by hand - this also clears the four qe-fig-002 and four qe-fig-005 hits and lets the 100-node exercise graph be drawn at all.Reconcile the definition of \(Q\) at 177-184 with the matrix at 201-207: the definition has no diagonal case, and
Q[6, 6] = 0is the entry that makes the iteration terminate. State the destination self-loop as part of the definition, and fix “principle diagonal” to “principal” at 210.Print the first few iterates \(J_0, J_1, J_2, \ldots\) for the 7-node graph so that “This sequence converges to \(J\)” (164) is shown rather than asserted; the loop at 227-236 already computes them.
Fix the note at 266-269: it names
np.equal(), which the lecture never uses - the main text usesnp.array_equal(232) and the solution usesnp.allclose(430).Restructure 74-86 so the two optimal paths read as one list: put the two figures side by side after both bullets, or caption each figure with its path, since as it stands each image sits above the bullet it illustrates.
Bold the cost-to-go function at 92 and the graph vocabulary at 61-66 (node/vertex, arrow/edge, weighted directed graph), to match distance matrix at 186.
Replace
np.zeros_like(nodes, dtype=int)andnp.empty_like(nodes, dtype=int)at 221-222 withnp.zeros(7, dtype=int)/np.empty(7, dtype=int), or say why the integer dtype is safe here given theinfentries inQ- the exercise note at 266-268 warns about exactly this.Clear the code whitespace: whitespace-only blank lines at 231 and 234, trailing space at 232,
return(J)at 436, and the single blank line before the def at 388. Leave the aligned matrix literal at 201-207 as it is - the sevenmultiple spaces after ','warnings there are the intended formatting.Trim the three overlapping application lists (30-34, 38-39, 70-72) to one, drop the two “* etc., etc.” bullets at 34 and 72, cut the restatement at 171-172, and rewrite 460 in the register of the rest of the lecture.
Tidy the
casesenvironment at 180-183: both rows begin with a stray&and no left-hand column, so both branches are pushed right; and consider movingQ[destination_node, destination_node] = 0out of the per-line loop at 404, where it is re-executed for every line of the file.