Robot Motion
Chapter 22PART VIFrontiers and IntegrationDifficulty: AdvancedEstimated reading time: 85 min

Learning and Planning

Where machine learning belongs in a motion planner, said without hype. Learned samplers, learned heuristics, neural planners and diffusion models all replace the proposal and never the verifier, so completeness and optimality are inherited from the classical stage and speed is bought from the learner at a measurable exchange rate.

The framework presented in this section enables a rigorous treatment of asymmetric reachability, nonmanifold configuration spaces, and sampling from arbitrary distributions.
Howie Choset, Kevin Lynch, Seth Hutchinson, George Kantor, Wolfram Burgard, Lydia Kavraki, and Sebastian ThrunPrinciples of Robot Motion (2005), §7.4.3

In this chapter

Every planner in Parts II through V was designed by hand. Uniform samples, Euclidean heuristics, Gaussian and bridge biases a person reasoned out from the geometry of narrow passages. This chapter asks the question every planning group asks in 2026, and tries to answer it without hype: where does machine learning belong in a motion planner?

The answer that organizes everything is one sentence. Learning replaces the proposal, never the verifier. A learned sampler changes where a PRM looks, never whether an edge is collision checked. A learned heuristic changes the order in which A* expands nodes, never the test that declares a path valid. A neural planner that emits waypoints is only as complete as the classical repair behind it, and a diffusion model that emits whole trajectories produces candidates that a checker still has to accept. In every case the guarantee — completeness, optimality, collision freedom — is inherited from the verifier, the speed is bought from the learner, and the exchange rate is a number you can measure.

Choset's abstract path-tiling theorem (§7.4.3, which the epigraph introduces) never needed uniform samples. We carry one constant λ\lambda through its proof, then measure what the learner actually buys on Reach's Workbench — less than the abstracts suggest, and more than nothing.

The problem: a sampler that learned the bench

Chapter 11's PRM draws configurations uniformly from Reach's torus. On a cluttered Workbench most of those samples land where the eventual path never goes, and each costs a collision check plus a dozen more for every edge tried. Someone who has watched a thousand roadmaps grow knows roughly where paths go; Chapter 11's Gaussian, bridge and OBPRM samplers were three attempts to write that knowledge down as a rule.

The modern move is not to write it down at all. Generate six hundred random Workbenches, run Chapter 11's planner on three queries in each, record every node of every path it found, and train a small network to predict, from the table's occupancy raster and the query's two endpoints, where on the torus the path nodes will be. Then sample from the network. The data is real: every recorded node passed Chapter 2's collision checker, every path was certified edge by edge. Nothing in the planner changes except the line that draws the next sample.

Here is what that buys, measured on thirty solvable queries on fresh Workbenches from the same random generator, every planner stopped at a fixed number of collision checks. At a budget of 100 checks the learned proposal — mixed with ten percent uniform, for a reason the mathematics will make non-negotiable — solves 60% of the queries and the uniform PRM solves 47%. At 400 checks the order reverses: the learned sampler solves 77% and uniform 90%. At 12,800 checks both solve every one. The paths the learned sampler finds are shorter (1.28 times the best known path against uniform's 1.33), and every millisecond of that is spent on a network that took 241 seconds of data generation and training before the first query, which no realistic number of queries on this suite pays back.

That is the chapter in miniature: the learner moved where the samples fell, could not change what the planner was able to find, and whether the move paid depends on budget, world and bookkeeping.

Building intuition: the scout and the surveyor

The chapter's picture in one line: the learner is a scout who says "look here"; the planner is the surveyor who still measures every edge. Each widget below puts a different scout in front of the same surveyor. Read them as a set — they are four instances of one design and one honest scoreboard.

Where the samples fall

Two Chapter 11 PRMs grow side by side on the same randomized Workbench, seed and query. The left pane draws uniformly; the middle pane draws from the learned proposal p^θ(q∣E,qstart,qgoal)\hat p_\theta(q \mid \mathcal{E}, \qstart, \qgoal) — the grey heat under the amber C-obstacle raster — mixed with uniform at weight λ\lambda; the right pane is the Workbench.

On the default world the learned pane joins start and goal after about half the collision checks the uniform pane spends: the heat sits on the band of the torus the path actually uses, and the samples follow it. Now press held-out generator. The world is replaced by one built from long thin bars at arbitrary angles — a family of shapes the training generator never produced — and on this seed the learned pane needs more than twice the checks uniform does. The heat still lands in confident blobs; they are just in the wrong place, because the network learned what blocks do to Reach's torus and is now looking at bars. Three things to notice, in order of importance.

Both panes connect. Slide the sample budget up and both roadmaps join on every solvable query you try, on both generators, at every λ\lambda the slider allows. The learner moves where the planner looks. It does not move what the planner can find — that is the content of Derivation 1, and the reason the λ\lambda slider stops at 0.02 instead of reaching zero.

The currency is collision checks. On Reach's torus a query is usually joined within a handful of nodes, so "samples until the join" hides the difference. The check count — the book's cost unit since Chapter 11 — shows it. The success-at-budget curves below the canvas accumulate over further seeds in the background, and they are the honest summary: on some worlds the learner wins big, on others it loses, and the curve is the average of both.

The bar chart is the chapter's first formula. It prints DKL⁡(p^ ∥ u)D_{\KL}(\hat p \,\|\, u) on the four quadrants of the torus, live: how concentrated the proposal is — not whether the concentration is in the right place, which takes a second divergence. The four-cell example computes both by hand.

A heuristic that knows too much

Chapter 6's A* on a 24 × 24 lattice of Reach's torus, with the admissible octile distance, a learned cost-to-go h^θ\hat h_\theta, or h^θ/(1+ϵval)\hat h_\theta / (1 + \epsilon_{val}). Left: explored set (blue), path (purple), a red mark wherever h^\hat h exceeds the true cost-to-go h∗h^* from backward Dijkstra. Right: every reachable cell at (h∗(v),h^(v))(h^*(v), \hat h(v)) against the diagonal.

On the default world the learned heuristic does exactly what the folklore says a better heuristic does: it expands fewer nodes than octile. And it returns a longer path — the cost-ratio tile reads above one. The scatter shows why: a cloud of red points above the diagonal, cells where the net guessed high. The measured ϵ\epsilon is the worst of those ratios on this world, and the fourth tile prints the guarantee (1+ϵ) C∗(1 + \epsilon)\,C^* that Derivation 3 proves the realized cost can never exceed. It never does — on this world, on any world you re-roll to, at any scale.

Then try the other two settings. Slide the scale above 1 and even the octile heuristic becomes inadmissible (that is weighted A*, which Chapter 6 introduced): expansions fall, the path stays at or above C∗C^*, and the bound line rises with the scale. Switch to the deflated heuristic and the red cloud vanishes, the path becomes optimal again — and the expansion count balloons to nearly three times octile's. Dividing by 1+ϵval1 + \epsilon_{val} buys admissibility on the validation worlds by throwing away most of what the net knew.

Across many worlds the summary is less flattering: on the audit worlds the learned heuristic and octile expand almost the same number of nodes (ratio 0.994). Octile is already good on this lattice — its mean ratio to h∗h^* on the validation worlds is 0.917, against the net's 0.959. What does not vary is the bound: the learned heuristic cannot hurt you by more than its measured ϵ\epsilon.

The net that hops

An MPNet-style policy proposes Reach's next configuration a hop η=0.35\eta = 0.35 rad away. Each hop goes to the verifier — Chapter 11's Steer, fulfilled by Chapter 2's sound swept check — and is drawn blue if certified, red and dashed if not. Rejected stretches are bridged by Chapter 12's RRT-Connect (the thin blue mini-tree); if local repairs run out, RRT-Connect runs on the whole original query.

On the training tab most of the hops pass. The policy learned the shape of paths on block-shaped Workbenches, and on this one it walks most of the way by itself; one short repair splices over the stretch where it clipped a block. Switch to the held-out tab: fewer than two-thirds of the hops pass, and the tile that splits the path into by net and by repair tips the other way — the repair planner supplies most of the length. Now turn repair off. The tile labelled net alone reads failed on both tabs. A path is only a path if every hop is certified and the last one reaches the goal; one clipped corner is enough to have nothing. The network did not learn to plan; it learned the training distribution of paths — valuable, since on a good day it proposes most of the answer — and with the repair planner behind it the composition is exactly as complete as RRT-Connect (Derivation 4).

Noise into paths

Diffusion models generate by running a noising process backward. The smallest example: a trajectory τ∈R16\tau \in \mathbb{R}^{16} is a configuration per time step, pinned to 0 at both ends; a slab blocks ∣q∣<0.3\lvert q \rvert < 0.3 during steps 5 through 10; demonstrations swing around it, half above and half below. Thirty-two samples start as pure noise and are denoised for K=40K = 40 steps by a small net, then checked: purple is clear, red went through the slab.

Scrub the step slider slowly from 0. For the first half of the steps nothing recognizable happens; then the swing appears, both swings, as the samples commit to going over or under. At step 40 a good fraction are red: plausible-looking trajectories that dip into the slab for a time step or two. Turn on cost guidance — each step also descends the gradient of a smooth cost that penalizes coming near the slab — and the red fraction drops a great deal. It does not reach zero, on any seed you try. A soft penalty is not a hard constraint, which is why Chapter 2's checker runs on every sample and why a diffusion planner is a proposal in exactly the sense of the sampler above.

The board

Seven planners — four hand-designed PRM samplers, the learned sampler at λ=0.1\lambda = 0.1, RRT-Connect, and the MPNet-style planner — on thirty solvable queries from each generator, same world, query and seed, stopped at 12,800 collision checks; smaller budgets are read off the same runs.

Toggle the suite and move the budget. The learned rows (tinted orange) win at the smallest budgets in distribution and then fall behind; their first paths are consistently shorter; their wall-clock is worse. RRT-Connect, which nobody trained, beats every PRM row at almost every budget on both suites. Drag the amortization slider to 10410^4 queries and the learned rows still do not overtake uniform on wall-clock: they were never faster per query. Putting it together reads this board line by line.

The mathematics

Notation used in this chapter
SymbolMeaningNote
E\mathcal{E}environment encoding fed to a net — here an 8 × 8 occupancy raster of the Workbench, each cell the fraction of four probe points inside an obstacle
u(q)=1/μ(Q)u(q) = 1/\mu(\mathcal{Q})the uniform density on 𝒬 — Chapter 11's default proposalChoset §7.4.1
p^θ(q∣E),  p∗(q∣E)\hat p_\theta(q \mid \mathcal{E}),\; p^*(q \mid \mathcal{E})the learned proposal; the target — the empirical distribution of nodes on Chapter 11 roadmap paths
pλ=(1−λ)p^θ+λup_\lambda = (1 - \lambda)\hat p_\theta + \lambda uthe bounded-below mixture, λ ∈ (0, 1]
DKL⁡(p ∥ q)=∑ipiln⁡(pi/qi)D_{\KL}(p \,\|\, q) = \sum_i p_i \ln(p_i/q_i)Kullback–Leibler divergence on a partition, in nats (bits in parentheses); H(p) = −Σ p_i ln p_i is the entropy
h∗(v),  h^θ(v),  ϵh^*(v),\; \hat h_\theta(v),\; \epsilontrue cost-to-go; learned heuristic; the smallest ε with ĥ ≤ (1 + ε) h* on the audited nodes
πθ,  η\pi_\theta,\; \etaa waypoint policy (neural planner); its hop length on T²
βk,  αˉk,  ϵθ(τk,k,E),  J(τ)\beta_k,\; \bar\alpha_k,\; \epsilon_\theta(\tau^k, k, \mathcal{E}),\; J(\tau)diffusion noise schedule and its cumulative product; the noise predictor over a trajectory τ ∈ 𝒬^T; the guidance costChapter 19 for J

Proposals and verifiers

Everything in this chapter is an instance of two definitions and one composition.

The constant in Definition 22.2 is the constant in the failure bound. The shipped sampler's raw softmax leaves 188 of 3,456 cells, across 24 audited worlds, below a thousandth of the uniform mass: positive, and useless as a guarantee. The mixture replaces that unknowable minimum by λ\lambda.

Completeness survives a floor

Recall Chapter 11's statement of Choset's Theorem 7.4.3: if PRM with local planner relation RR and sampling measure μ\mu is probabilistically complete, there are ℓ\ell sets A1,…,Aℓ⊂QfreeA_1, \dots, A_\ell \subset \Qfree, each of positive measure, such that any choice xj∈Ajx_j \in A_j chains into a path xRx1R⋯RxℓRyx \mathrel{R} x_1 \mathrel{R} \cdots \mathrel{R} x_\ell \mathrel{R} y; and with p=min⁡jμ(Aj)p = \min_j \mu(A_j) the failure probability after nn samples is at most ℓ(1−p)n≤ℓe−pn\ell(1-p)^n \le \ell e^{-pn}. Choset introduces the section by saying the framework covers "sampling from arbitrary distributions", and the proof bears it out: the only property of μ\mu it uses is a lower bound on the mass of each tile.

Pr⁡[FAILURE after n samples from pλ]  ≤  ℓ (1−λ pu)n  ≤  ℓ e−λ pu n,pu=min⁡ju(Aj).\Pr\big[\text{FAILURE after } n \text{ samples from } p_\lambda\big] \;\le\; \ell\,\big(1 - \lambda\, p_u\big)^n \;\le\; \ell\, e^{-\lambda\, p_u\, n}, \qquad p_u = \min_j u(A_j).
DerivationDerivation 1 — probabilistic completeness survives any bounded-below proposal

Step 1 — take the uniform tiles. Suppose uniform PRM with relation RR is probabilistically complete on the query (x,y)(x, y) (Chapter 11 proved this for the straight-line planner on any space with a clearance-positive path). Theorem 7.4.3 supplies tiles A1,…,AℓA_1, \dots, A_\ell with pu=min⁡ju(Aj)>0p_u = \min_j u(A_j) > 0 and the chaining property. The chaining property is a statement about RR alone — it does not mention the sampling measure.

Step 2 — failure needs an empty tile. If every AjA_j receives at least one of the nn roadmap nodes, the roadmap contains a path from xx to yy (the nodes chain by Step 1, and PRM's connection rule finds the chain under the same hypotheses as in Chapter 11). So {FAILURE}⊆⋃j{Aj empty}\{\text{FAILURE}\} \subseteq \bigcup_j \{A_j \text{ empty}\}.

Step 3 — each draw hits a tile with probability at least λpu\lambda p_u. Under the mixture, pλ(Aj)=(1−λ)p^θ(Aj)+λu(Aj)≥λu(Aj)≥λpup_\lambda(A_j) = (1-\lambda)\hat p_\theta(A_j) + \lambda u(A_j) \ge \lambda u(A_j) \ge \lambda p_u, because p^θ(Aj)≥0\hat p_\theta(A_j) \ge 0 — the only thing we know about the network. The learned sampler is conditioned on the query, which is fixed before sampling begins, so the nn draws are independent and identically distributed. Rejection of colliding draws only helps: the tiles lie in Qfree\Qfree, so conditioning on acceptance can only raise their probability.

Step 4 — union bound. Pr⁡[Aj empty]≤(1−λpu)n\Pr[A_j \text{ empty}] \le (1 - \lambda p_u)^n, and Pr⁡[FAILURE]≤∑jPr⁡[Aj empty]≤ℓ(1−λpu)n≤ℓe−λpun\Pr[\text{FAILURE}] \le \sum_j \Pr[A_j \text{ empty}] \le \ell (1-\lambda p_u)^n \le \ell e^{-\lambda p_u n}, using 1−t≤e−t1 - t \le e^{-t}.

Step 5 — what was used, and what λ=0\lambda = 0 does. Only the lower bound pλ≥λup_\lambda \ge \lambda u was used; the network could be anything, including adversarial. The price is visible in the exponent: the sample count for a given confidence is at most 1/λ1/\lambda times uniform's. Setting λ=0\lambda = 0 removes Step 3, and with it the bound: if the proposal puts zero mass on a corridor every path must cross, the failure probability is 1 for every nn. The theorem survives a learner. It does not survive a learner with no floor — which is why the Rust constructor refuses λ≤0\lambda \le 0 and the widget's slider stops at 0.02. ■\blacksquare

Hitting times, divergences, and maximum likelihood

Derivation 1 is a worst-case statement — the learner can only lose a factor 1/λ1/\lambda. What it can win is a statement about where its mass is.

DerivationDerivation 2 — hitting time, KL as concentration and aim, and maximum likelihood

Step 1 — geometric waiting time. The number of independent draws until the first one lands in a region AA is geometric with success probability p(A)p(A), so its mean is 1/p(A)1/p(A). If the proposal puts p^(A)=a u(A)\hat p(A) = a\, u(A) on a corridor AA, it hits the corridor aa times sooner than uniform.

Step 2 — the mixture caps the loss. Under pλp_\lambda, pλ(A)≥λu(A)p_\lambda(A) \ge \lambda u(A), so the mean wait is at most 1/(λu(A))1/(\lambda u(A)) — never more than 1/λ1/\lambda times uniform's, however wrong p^\hat p is about AA. At λ=0.1\lambda = 0.1 that is a factor of ten — the regime the Board's 400-check column shows, where the queries on which the proposal aimed elsewhere are still waiting for their corridor.

Step 3 — concentration. Coarsen Q\Q into NN cells with ui=1/Nu_i = 1/N. Then DKL⁡(p^ ∥ u)=∑ip^iln⁡(Np^i)=ln⁡N−H(p^)D_{\KL}(\hat p \,\|\, u) = \sum_i \hat p_i \ln(N \hat p_i) = \ln N - H(\hat p): zero for the uniform proposal, ln⁡N\ln N for a proposal that puts everything in one cell. It measures how peaked the proposal is, and nothing else.

Step 4 — aim. Let p∗p^* be the distribution of path nodes on the same partition. Then DKL⁡(p∗ ∥ p^)D_{\KL}(p^* \,\|\, \hat p) is small exactly when p^\hat p has mass wherever paths go. The direction matters: p∗p^* in the first slot makes the divergence infinite if p^i=0\hat p_i = 0 for any cell a path uses, and indifferent to mass that p^\hat p wastes elsewhere — the "forward" divergence penalizes the sampler's one catastrophic failure, missing the corridor, and forgives its mild one, sampling where paths do not go.

Step 5 — maximum likelihood. Training data are path nodes q1,…,qM∼p∗q_1, \dots, q_M \sim p^*. The log-likelihood per sample is 1M∑jln⁡p^θ(qj)\frac{1}{M}\sum_j \ln \hat p_\theta(q_j), whose expectation is ∑ipi∗ln⁡p^θ,i=−H(p∗)−DKL⁡(p∗ ∥ p^θ)\sum_i p^*_i \ln \hat p_{\theta,i} = -H(p^*) - D_{\KL}(p^* \,\|\, \hat p_\theta). The entropy H(p∗)H(p^*) does not depend on θ\theta, so maximizing the likelihood — equivalently, minimizing the cross-entropy loss the network is trained on — is minimizing the aim divergence. Conditioning on E\mathcal{E} and the query changes nothing but the subscripts. ■\blacksquare

The training curve in these units. Uniform's cross-entropy on 144 cells is ln⁡144=4.970\ln 144 = 4.970 nats. The shipped sampler's final training cross-entropy is 3.393 nats, so by Step 5 it sits 1.576 nats closer to the path distribution, in DKL⁡(p∗ ∥ ⋅)D_{\KL}(p^* \,\|\, \cdot), than uniform does — on its own training worlds. On fresh worlds from the same generator the check ch22 aim measures it directly: with λ=0.1\lambda = 0.1 and one fresh PRM path per world as p∗p^*, the mixture is closer to p∗p^* than uniform on 6 of 7 worlds (mean 1.946 nats against 2.729). On the held-out bars it is closer on 8 of 10 (2.186 against 2.770).

DerivationCollapsible — the CVAE of Ichter, Harrison and Pavone, and why this chapter uses cells

On Reach's two-dimensional torus a categorical over a 12 × 12 grid is a complete description of any density at that resolution, and it is natively multimodal: two corridors are two bright groups of cells. In seven dimensions a grid has 127≈3.6×10712^7 \approx 3.6 \times 10^7 cells and the idea dies. Ichter, Harrison and Pavone's answer is a conditional variational autoencoder: a latent z∼N(0,I)z \sim \Normal(0, I) and a decoder q=gθ(z,y)q = g_\theta(z, y) conditioned on y=(E,qstart,qgoal)y = (\mathcal{E}, \qstart, \qgoal), trained by maximizing the evidence lower bound

ln⁡pθ(q∣y)  ≥  Ez∼rϕ(z∣q,y)[ln⁡pθ(q∣z,y)]−DKL⁡(rϕ(z∣q,y) ∥ N(0,I))\ln p_\theta(q \mid y) \;\ge\; \E_{z \sim r_\phi(z \mid q, y)}\big[\ln p_\theta(q \mid z, y)\big] - D_{\KL}\big(r_\phi(z \mid q, y) \,\|\, \Normal(0, I)\big)

with an encoder rϕr_\phi used only during training (Kingma and Welling's construction, with conditioning). The latent is what lets one network propose both corridors of a world: different regions of zz-space decode to different modes. Maximizing the bound is maximizing a lower bound on the likelihood, so Step 5 applies with an inequality, and the learned proposal is used in a mixture with uniform exactly as here — their paper mixes in uniform samples for the same reason Derivation 1 gives. The Rust crate keeps the categorical for T2T^2 and names the CVAE as the route to higher dimensions; the KL on a partition is then an estimate rather than exact.

The four-cell example, by hand

Coarsen Reach's torus on one Workbench into its four quadrants. Two hundred draws of the raw learned sampler (λ=0\lambda = 0) fall (100,60,30,10)(100, 60, 30, 10), so p^=(0.50,0.30,0.15,0.05)\hat p = (0.50, 0.30, 0.15, 0.05) against u=(0.25,0.25,0.25,0.25)u = (0.25, 0.25, 0.25, 0.25):

DKL⁡(p^ ∥ u)=0.5ln⁡2+0.3ln⁡1.2+0.15ln⁡0.6+0.05ln⁡0.2=0.34657+0.05470−0.07662−0.08047=0.2442 nats (0.352 bits).D_{\KL}(\hat p \,\|\, u) = 0.5\ln 2 + 0.3\ln 1.2 + 0.15\ln 0.6 + 0.05\ln 0.2 = 0.34657 + 0.05470 - 0.07662 - 0.08047 = \mathbf{0.2442\ \text{nats}}\ (0.352\ \text{bits}).

Equivalently ln⁡4−H(p^)\ln 4 - H(\hat p); the check asserts the identity to 10−1210^{-12}. Now the aim. The same world's roadmap paths put their nodes at p∗=(0.55,0.30,0.10,0.05)p^* = (0.55, 0.30, 0.10, 0.05), and only two terms survive because the other two ratios are one:

DKL⁡(p∗ ∥ p^)=0.55ln⁡1.1+0.10ln⁡23=0.0524−0.0405=0.0119,DKL⁡(p∗ ∥ u)=ln⁡4−H(p∗)=1.38629−1.07005=0.3162.D_{\KL}(p^* \,\|\, \hat p) = 0.55\ln 1.1 + 0.10\ln\tfrac{2}{3} = 0.0524 - 0.0405 = \mathbf{0.0119}, \qquad D_{\KL}(p^* \,\|\, u) = \ln 4 - H(p^*) = 1.38629 - 1.07005 = \mathbf{0.3162}.

The learned proposal is about 27×27\times closer to the path distribution than uniform is. Finally the floor: at λ=0.1\lambda = 0.1 the mixture is pλ=0.9 p^+0.025=(0.475,0.295,0.160,0.070)p_\lambda = 0.9\,\hat p + 0.025 = (0.475, 0.295, 0.160, 0.070), whose smallest cell, 0.0700.070, is above λ/4=0.025\lambda/4 = 0.025 — Definition 22.2 holds, with room to spare, on the cell the raw sampler nearly ignored. The w22.1 bar chart runs this computation live on the current world.

Heuristics that overestimate

h^≤(1+ϵ) h∗  ⟹  CA*  ≤  (1+ϵ) C∗\hat h \le (1+\epsilon)\, h^* \;\Longrightarrow\; \htmlClass{term-path}{C_{\text{A*}}} \;\le\; (1 + \epsilon)\, C^*
DerivationDerivation 3 — ε-admissible A* with reopening is (1 + ε)-optimal

Step 1 — the open optimal node. Let P∗=(qstart=v0,v1,…,vm=qgoal)P^* = (\qstart = v_0, v_1, \dots, v_m = \qgoal) be an optimal path of cost C∗C^*. Before the goal is expanded, some node of P∗P^* is on OPEN with its optimal gg: take the first viv_i on P∗P^* that is not closed with g(vi)=g∗(vi)g(v_i) = g^*(v_i). Its predecessor vi−1v_{i-1} was expanded with g(vi−1)=g∗(vi−1)g(v_{i-1}) = g^*(v_{i-1}), which relaxed viv_i to g(vi)≤g∗(vi−1)+c(vi−1,vi)=g∗(vi)g(v_i) \le g^*(v_{i-1}) + c(v_{i-1}, v_i) = g^*(v_i); so g(vi)=g∗(vi)g(v_i) = g^*(v_i), and viv_i is on OPEN — if it had been closed with a worse gg, the relaxation reopened it. (This is where reopening is used.)

Step 2 — its key is at most (1+ϵ)C∗(1+\epsilon)C^*. f(vi)=g∗(vi)+h^(vi)≤g∗(vi)+(1+ϵ) h∗(vi)≤(1+ϵ)(g∗(vi)+h∗(vi))=(1+ϵ) C∗f(v_i) = g^*(v_i) + \hat h(v_i) \le g^*(v_i) + (1+\epsilon)\, h^*(v_i) \le (1+\epsilon)\big(g^*(v_i) + h^*(v_i)\big) = (1+\epsilon)\, C^*, using g∗≥0g^* \ge 0 and g∗(vi)+h∗(vi)=C∗g^*(v_i) + h^*(v_i) = C^* on an optimal path.

Step 3 — the goal pops with key CC. When the goal is popped, f(qgoal)=g(qgoal)+0=Cf(\qgoal) = g(\qgoal) + 0 = C, the cost of the path A* returns.

Step 4 — best-first order. A* pops the minimum key. At the moment the goal pops, viv_i from Step 1 is still on OPEN with key at most (1+ϵ)C∗(1+\epsilon)C^*, so C≤f(vi)≤(1+ϵ)C∗C \le f(v_i) \le (1+\epsilon) C^*.

Step 5 — conclusion. The returned cost satisfies C≤(1+ϵ)C∗C \le (1+\epsilon)C^*. Nothing about how h^\hat h was produced entered the proof: a learned heuristic, a scaled octile distance (weighted A*), and a hand-tuned guess are all covered, with ϵ\epsilon the measured worst ratio. ■\blacksquare

The additive form. If instead h^≤h∗+δ\hat h \le h^* + \delta, Step 2 gives f(vi)≤C∗+δf(v_i) \le C^* + \delta and the same argument gives C≤C∗+δC \le C^* + \delta (Exercise 2).

The reopening caveat. Without reopening, Step 1 fails: an inconsistent heuristic can close a node of P∗P^* with a suboptimal gg that is never repaired, and the bound — even at ϵ=0\epsilon = 0 — no longer holds. Chapter 6's engine reopens, counts the reopenings, and the widget prints the count.

The measurement caveat. The proof needs h^≤(1+ϵ)h∗\hat h \le (1+\epsilon)h^* on the nodes of this search. A validation ϵ\epsilon is a maximum over the audited worlds; it is a guarantee there and an estimate everywhere else.

The three-node audit. A chain a→2b→2c→1goala \xrightarrow{2} b \xrightarrow{2} c \xrightarrow{1} \text{goal} has true costs-to-go h∗=(5.0,3.0,1.0)h^* = (5.0, 3.0, 1.0), and a net outputs h^=(4.6,3.4,0.8)\hat h = (4.6, 3.4, 0.8). The ratios are h^/h∗=(0.92,1.1333,0.80)\hat h/h^* = (0.92, 1.1333, 0.80): admissible at aa and cc, violated at bb, where 3.4>3.03.4 > 3.0. Consistency fails at bb too, since h^(b)=3.4>c(b,c)+h^(c)=2.8\hat h(b) = 3.4 > c(b, c) + \hat h(c) = 2.8. So

ϵ=max⁡(0.92, 1.1333, 0.80)−1=0.1333,any path A* returns from a costs at most 1.1333×5.0=5.667.\epsilon = \max(0.92,\ 1.1333,\ 0.80) - 1 = \mathbf{0.1333}, \qquad \text{any path A* returns from } a \text{ costs at most } 1.1333 \times 5.0 = \mathbf{5.667}.

The audit is one function, admissibility_audit, and the check asserts the violation set {b}\{b\}, the inconsistency set {b}\{b\}, ϵ\epsilon and the bound.

The audit of the shipped heuristic. The net predicts ln⁡(h∗/hoct)\ln(h^*/h_{oct}), the factor by which obstacles stretch the empty-torus octile distance, so h^=hoct ey^\hat h = h_{oct}\,e^{\hat y} is inadmissible wherever the net guesses high. It was trained on 71,848 backward-Dijkstra labels from 240 worlds, with overestimates weighted six times in the squared loss. Auditing every reachable cell of 16 validation worlds, three goals each: 5,584 of 12,830 cells over, ϵval=0.662\epsilon_{val} = 0.662. On 16 held-out bar worlds, ϵ=0.387\epsilon = 0.387 — smaller: a sample maximum moves around. The check ch22 ε audit repeats this on eight fresh worlds (ϵ=0.327\epsilon = 0.327) and runs 43 searches: every one returned an optimal path. Derivation 3 is a ceiling, not a forecast.

Neural planners and what they inherit

DerivationDerivation 4 — a proposal plus a verifier inherits the verifier's guarantee

Step 1 — case split. For a query (qstart,qgoal)(\qstart, \qgoal) the composition either returns a candidate whose every segment the verifier certified, or hands the original query to a fallback planner F\mathcal{F}.

Step 2 — validity. In the first case the path is collision-free because the verifier is sound (the swept check of Chapter 2 cannot miss a collision; a fixed-step subdivision can, and the neural planner uses the former for that reason). In the second case it is collision-free because F\mathcal{F}'s paths are.

Step 3 — completeness. The composition fails only if the proposal is rejected and F\mathcal{F} fails on (qstart,qgoal)(\qstart, \qgoal). So Pr⁡[fail]≤Pr⁡[F fails]\Pr[\text{fail}] \le \Pr[\mathcal{F} \text{ fails}]: if F\mathcal{F} is probabilistically complete with Chapter 12's exponential rate, so is the composition. The guarantee G\mathcal{G} is a statement about the query, and the fallback answers the same query.

Step 4 — what does not transfer. Time. The composition's running time is the proposal's plus the verification's plus, in the bad case, the whole fallback's; its worst case is worse than F\mathcal{F}'s alone. Without the fallback the composition has no guarantee at all — only an empirical success rate measured on some distribution of worlds. ■\blacksquare

MPNet's schedule. Qureshi and colleagues' planner proposes bidirectionally, contracts the path, re-plans failed segments with the network and finally with a classical planner. Ours is the one-directional version; Derivation 4 only needs the last resort to run on the original query.

The check ch22 proposal + verifier runs the planner on 16 queries, eight from each generator, drawn without asking whether they have an answer: 12 returned a path, all 12 passed an independent dense recheck at a step of 0.005 rad, and 4 queries went to the whole-query fallback. The network alone solved 0 of the 8 in-distribution queries and 4 of the 8 held-out ones. Eight queries are too few to say more than that the success rate without repair is not a number anyone should plan on.

Diffusion planners, at the level of statements

DerivationDerivation 5 — the denoising objective and guided sampling (statement level)

Step 1 — noising destroys structure. The forward process q(τk∣τk−1)=N(1−βk τk−1, βkI)q(\tau^k \mid \tau^{k-1}) = \Normal\big(\sqrt{1-\beta_k}\,\tau^{k-1},\ \beta_k I\big) has the closed form τk=αˉk τ0+1−αˉk ϵ\tau^k = \sqrt{\bar\alpha_k}\,\tau^0 + \sqrt{1-\bar\alpha_k}\,\epsilon with αˉk=∏i≤k(1−βi)\bar\alpha_k = \prod_{i \le k}(1-\beta_i) and ϵ∼N(0,I)\epsilon \sim \Normal(0, I). The toy model uses K=40K = 40 steps with βk\beta_k linear from 10−310^{-3} to 0.250.25, so αˉK=4.06×10−3\bar\alpha_K = 4.06 \times 10^{-3}: less than half a percent of the demonstration survives, and τK\tau^K is noise.

Step 2 — the net reverses one step. A network ϵθ(τk,k,E)\epsilon_\theta(\tau^k, k, \mathcal{E}) is trained to predict the noise, by minimizing Eτ0,k,ϵ∥ϵ−ϵθ(τk,k,E)∥2\E_{\tau^0, k, \epsilon}\lVert \epsilon - \epsilon_\theta(\tau^k, k, \mathcal{E})\rVert^2 over demonstrations τ0\tau^0. Ho, Jain and Abbeel showed that this objective is a reweighted variational bound on the likelihood; at statement level it is "learn to undo one step of noise".

Step 3 — chaining samples the demonstrations. Ancestral sampling runs τk−1=11−βk(τk−βk1−αˉk ϵθ(τk,k,E))+βk z\tau^{k-1} = \frac{1}{\sqrt{1-\beta_k}}\big(\tau^k - \frac{\beta_k}{\sqrt{1-\bar\alpha_k}}\,\epsilon_\theta(\tau^k, k, \mathcal{E})\big) + \sqrt{\beta_k}\, z from k=Kk = K down to 1, with z∼N(0,I)z \sim \Normal(0, I) (no noise on the last step) and the start and goal re-pinned after every step — Janner and colleagues' inpainting of the constraints. If the demonstrations are bimodal, so are the samples: nothing in the procedure averages the two swings into one that goes through the slab.

Step 4 — guidance tilts, it does not constrain. Subtracting wk∇τJ(τk)w_k \nabla_\tau J(\tau^k) from each step's mean, with wk∝βkw_k \propto \beta_k, approximately samples pθ(τ)exp⁡(−J(τ))p_\theta(\tau)\exp(-J(\tau)) — the demonstrations reweighted toward low cost. In the toy, JJ is Chapter 19's smoothness term plus a soft penalty ∑tmax⁡(0,m−∣τt∣)2\sum_t \max(0, m - \lvert\tau_t\rvert)^2 inside the slab's time window. A soft penalty can be outweighed and a gradient step can overshoot; collision is a hard constraint, so the checker runs afterwards. ■\blacksquare

On 64 samples the toy diffuser produces 58% valid trajectories unguided — 30 swinging above the slab, 34 below — and 83% with guidance. Both numbers are high, both modes survive, and neither is one. That last fact is the entire practical content of the section.

What an honest benchmark is

For (iii) the Rust harness uses RRT*'s incumbent after ten seconds; the web port uses the shortest path any method found, including a dense reference PRM — weaker (1.0 means "as good as anything we tried"), but every ratio is at least one by construction, as ch22 Board invariants asserts.

The algorithm

Five boxes, one per learned component and one for the board. Each is a classical algorithm from an earlier chapter with one line replaced; the replaced line is marked.

AlgorithmLEARNED-PROPOSAL PRM (Choset Algorithm 6, sampling line replaced)Costone forward pass, then BASIC PRM: per node one categorical draw (O(log N) by CDF search) plus k calls to Δ
In
Workbench encoding 𝓔, query (q_start, q_goal), trained net θ, floor λ ∈ (0, 1], budget b in collision checks, k
Out
a roadmap path from q_start to q_goal, or FAILURE
  1. π←softmax(fθ(E,qstart,qgoal))\pi \leftarrow \mathrm{softmax}\big(f_\theta(\mathcal{E}, \qstart, \qgoal)\big) — the cell masses, computed once per query
  2. if λ≤0\lambda \le 0 then return error — Derivation 1 needs a floor
  3. add qstart,qgoal\qstart, \qgoal as nodes; connect each to its kk nearest
  4. while qstart,qgoal\qstart, \qgoal are in different components and checks spent <b< b do
  5.     with probability λ\lambda: q←q \leftarrow uniform on Q\Q; otherwise draw cell c∼πc \sim \pi and q←q \leftarrow uniform in cell cc ◀ replaced line
  6.     if qq is collision-free then add qq; connect to its kk nearest with Δ\Dist (unchanged verifier)
  7. return the shortest roadmap path, or FAILURE
AlgorithmA*_ε WITH A MEASURED ε (Choset Algorithm 24 with reopening, heuristic replaced)Costaudit: one backward Dijkstra per audited goal plus one forward pass per cell; search: Dijkstra's worst case
In
lattice G, start s, goal g, net θ, encoding 𝓔, audit worlds W
Out
a path of cost C ≤ (1 + ε)C* on audited worlds, and ε
  1. audit: for each world in WW and each sampled goal g′g': h∗←h^* \leftarrow backward Dijkstra from g′g'
  2.     ϵ←max⁡(ϵ, max⁡vh^θ(v)/h∗(v)−1)\epsilon \leftarrow \max\big(\epsilon,\ \max_v \hat h_\theta(v)/h^*(v) - 1\big) over reachable vv with h∗(v)>0h^*(v) > 0
  3. search: h^(v)←hoct(v) exp⁡(y^θ(E,v,g))\hat h(v) \leftarrow h_{oct}(v)\, \exp\big(\hat y_\theta(\mathcal{E}, v, g)\big) for every free vv ◀ replaced line
  4. run A* from ss to gg with key f=g+h^f = g + \hat h, reopening any closed node whose gg improves
  5. return path, its cost CC, expansions, reopenings, and ϵ\epsilon — the bound (1+ϵ)C∗(1+\epsilon)C^* holds on the audited worlds (Derivation 3)
AlgorithmMPNET-STYLE PLAN WITH LAZY REPAIR (proposal: policy; verifier: Steer; fallback: Choset Algorithm 13)Costat most H forward passes, one Steer per hop, then repair trees
In
encoding 𝓔, query (q_start, q_goal), policy π_θ, hop η, repair budget r, fallback budget F
Out
a certified path, or FAILURE
  1. w0←qstartw_0 \leftarrow \qstart; for i=1,…,Hi = 1, \dots, H: wi←wi−1+ηtanh⁡πθ(E,wi−1,qgoal)w_i \leftarrow w_{i-1} + \eta \tanh \pi_\theta(\mathcal{E}, w_{i-1}, \qgoal), snapping to qgoal\qgoal within η\eta; re-propose with jitter if wiw_i collides ◀ proposal
  2. for each hop (wi−1,wi)(w_{i-1}, w_i): oki←Δ(wi−1,wi)≠\mathit{ok}_i \leftarrow \Dist(w_{i-1}, w_i) \ne NIL (swept, sound)
  3. walk the hops; at a failed hop, find the next free waypoint wjw_j (or qgoal\qgoal) and run RRT-Connect from wi−1w_{i-1} to wjw_j for rr iterations; splice the bridge
  4. if any bridge failed then run RRT-Connect on (qstart,qgoal)(\qstart, \qgoal) for FF iterations — the guarantee (Derivation 4)
  5. re-certify every segment of the result; return it, or FAILURE
AlgorithmGUIDED DENOISING, THEN VERIFY (Diffuser-style, statement level)CostK forward passes per sample (O(K T c_net)), then one hard check per sample
In
noise predictor ε_θ, schedule β₁…β_K, guidance cost J and scale s, endpoints, sample count M
Out
the certified subset of M sampled trajectories
  1. for m=1,…,Mm = 1, \dots, M: τ←N(0,I)\tau \leftarrow \Normal(0, I); pin τ0=qstart\tau_0 = \qstart, τT−1=qgoal\tau_{T-1} = \qgoal
  2.     for k=K,…,1k = K, \dots, 1: μ←11−βk(τ−βk1−αˉkϵθ(τ,k))−s βk∇J(τ)\mu \leftarrow \frac{1}{\sqrt{1-\beta_k}}\big(\tau - \frac{\beta_k}{\sqrt{1-\bar\alpha_k}}\epsilon_\theta(\tau, k)\big) - s\,\beta_k \nabla J(\tau)
  3.         τ←μ+βk z\tau \leftarrow \mu + \sqrt{\beta_k}\,z (no noise at k=1k = 1); re-pin the endpoints
  4. return {τ:τ\{\tau : \tau passes the collision checker}\} — the verifier is not optional
AlgorithmHONEST BENCHMARK (Definition 22.6)CostO(suites × n × planners × b_m)
In
two world generators, n queries per suite, planners, budgets b₁ < … < b_m, seed
Out
per planner and suite: success at each budget, cost ratio, wall-clock with and without inference
  1. for each suite and seed: build a world; draw a blocked query; keep it only if a dense reference PRM solves it
  2.     for each planner: run once with the same seed, stopping at bmb_m checks or at the first verified path
  3.         record checks at the first path, its cost after shortcutting, wall-clock, inference time
  4.     incumbent ←\leftarrow the shortest cost any run, or the reference, found
  5. report success at bib_i as the fraction with checks ≤bi\le b_i; cost ratio over the solved; time amortized over kk queries

Implementation in Rust

The learn crate trains with candle and infers without it. Training is native-only (candle-core and candle-nn build the networks, losses and optimizer); each trained network is exported to safetensors and evaluated by infer.rs, a forty-line forward pass on nalgebra, so the WebAssembly widgets and the native planner run identical arithmetic — a test asserts candle and infer.rs agree to 10−610^{-6} on a thousand inputs. The TypeScript port that runs this page goes one step further and hand-rolls a tiny MLP, backpropagation included, in lib/learn/mlp.ts — two layers, tanh, Adam — so the checks can train small networks from scratch (XOR, a one-dimensional function, the toy denoiser) and the whole story runs with no dependency. The check ch22 MLP backprop compares its gradients to central differences on three architectures; the worst relative discrepancy is 3×10−83 \times 10^{-8}. The crate imports and never re-defines Chapter 11's Sampler, Hybrid, Uniform and Steer, Chapter 12's RrtConnect and Chapter 6's astar.

crates/learn/src/{infer.rs, info.rs}
use nalgebra::{DMatrix, DVector};

/// A dense tanh MLP as exported from candle: layers (W, b), tanh between, linear out.
/// This is the *only* inference path — native planner, WASM widgets and the TS port agree.
pub struct MlpWeights { pub layers: Vec<(DMatrix<f32>, DVector<f32>)> }

impl MlpWeights {
    pub fn from_safetensors(bytes: &[u8]) -> Result<Self, LearnError> { /* names l0.w, l0.b, … */ }

    pub fn forward(&self, x: &DVector<f32>) -> DVector<f32> {
        let last = self.layers.len() - 1;
        self.layers.iter().enumerate().fold(x.clone(), |h, (i, (w, b))| {
            let z = w * h + b;
            if i == last { z } else { z.map(f32::tanh) }
        })
    }
}

pub fn softmax(z: &DVector<f32>) -> DVector<f64> {
    let m = z.max();                                   // shift: exp never overflows
    let e = z.map(|v| f64::from(v - m).exp());
    let s = e.sum();
    e / s
}

/// Counts on N cells and the two divergences of Derivation 2 (nats).
pub struct CoarseHistogram<const N: usize>(pub [u64; N]);

impl<const N: usize> CoarseHistogram<N> {
    pub fn probabilities(&self) -> [f64; N] {
        let t = self.0.iter().sum::<u64>() as f64;
        self.0.map(|c| c as f64 / t)
    }
    /// D_KL(p̂ ‖ u) = ln N − H(p̂): concentration, not aim.
    pub fn kl_to_uniform(&self) -> f64 {
        let u = [1.0 / N as f64; N];
        kl(&self.probabilities(), &u)
    }
    /// D_KL(target ‖ p̂): aim. Infinite if p̂ misses a cell the target uses — the failure that matters.
    pub fn kl_from(&self, target: &[f64; N]) -> f64 { kl(target, &self.probabilities()) }
    /// p_λ = (1 − λ) p̂ + λ u, cell by cell (Definition 22.2).
    pub fn mixture(&self, lambda: f64) -> [f64; N] {
        self.probabilities().map(|p| (1.0 - lambda) * p + lambda / N as f64)
    }
}

pub fn kl(p: &[f64], q: &[f64]) -> f64 {
    p.iter().zip(q).filter(|(pi, _)| **pi > 0.0).map(|(pi, qi)| {
        if *qi <= 0.0 { f64::INFINITY } else { pi * (pi / qi).ln() }       // 0 · ln 0 = 0
    }).sum()
}

The sampler is a categorical over a 12 × 12 coarsening of the torus, conditioned on the 8 × 8 raster and on (cos⁡,sin⁡)(\cos, \sin) of the four query angles — the wrap-aware encoding Chapter 5 recommends for S1S^1 factors. It implements Chapter 11's Sampler and nothing else; the free-space test inside sample is the one every other sampler runs. The safe version is not a new type: it is Chapter 11's Hybrid, and the constructor is where Derivation 1 is enforced.

crates/learn/src/sampler.rs
use candle_core::{DType, Device, Tensor};
use candle_nn::{linear, loss::cross_entropy, AdamW, Module, Optimizer, ParamsAdamW, VarBuilder, VarMap};
use manifold::{Manifold, T2};
use rand::{rngs::SmallRng, Rng};
use sampling::{FreeSpace, Hybrid, Sampler, Uniform};
use crate::{encode::{query_features, EnvEncoding}, infer::{softmax, MlpWeights}, LearnError};

pub const CELLS_N: usize = 12;
pub const CELLS: usize = CELLS_N * CELLS_N;
type Q = <T2 as Manifold>::Point;

/// D22.1. The raw proposal p̂_θ(· | 𝓔, q_start, q_goal): draw a cell, then a uniform point in it.
pub struct LearnedSampler { probs: Vec<f64>, cdf: Vec<f64> }

impl LearnedSampler {
    /// One forward pass per query — the cell masses do not depend on the samples drawn.
    pub fn new(net: &MlpWeights, env: &EnvEncoding, start: &Q, goal: &Q) -> Self {
        let probs: Vec<f64> = softmax(&net.forward(&query_features(env, start, goal))).iter().copied().collect();
        let cdf = probs.iter().scan(0.0, |acc, p| { *acc += p; Some(*acc) }).collect();
        Self { probs, cdf }
    }
    pub fn draw_raw(&self, rng: &mut SmallRng) -> Q {
        let u: f64 = rng.random();
        let c = self.cdf.partition_point(|&f| f < u).min(CELLS - 1);
        let (i, j) = (c % CELLS_N, c / CELLS_N);
        let w = std::f64::consts::TAU / CELLS_N as f64;
        T2::from_angles([-std::f64::consts::PI + (i as f64 + rng.random::<f64>()) * w,
                         -std::f64::consts::PI + (j as f64 + rng.random::<f64>()) * w])
    }
}

impl Sampler<T2> for LearnedSampler {
    fn sample(&mut self, _: &T2, free: &dyn FreeSpace<T2>, rng: &mut SmallRng) -> Option<Q> {
        let q = self.draw_raw(rng);
        free.is_free(&q).then_some(q)                  // the verifier is Chapter 11's, untouched
    }
}

/// D22.2 by construction: Chapter 11's `Hybrid` with `Uniform` as the floor at weight λ.
/// Derivation 1 needs λ > 0, so λ = 0 is an error here, not a warning in a README.
pub fn safe_learned_sampler(learned: LearnedSampler, lambda: f64) -> Result<Hybrid<T2>, LearnError> {
    if !(lambda > 0.0 && lambda <= 1.0) { return Err(LearnError::NoUniformFloor(lambda)); }
    Ok(Hybrid { parts: vec![(Box::new(learned), 1.0 - lambda), (Box::new(Uniform), lambda)] })
}

/// Maximum likelihood on path-node cells = min D_KL(p* ‖ p̂_θ) (Derivation 2). Native only.
pub fn train_sampler(x: &[Vec<f32>], cells: &[u32], epochs: usize) -> candle_core::Result<VarMap> {
    let dev = Device::Cpu;
    let vars = VarMap::new();
    let vb = VarBuilder::from_varmap(&vars, DType::F32, &dev);
    let (l0, l1) = (linear(x[0].len(), 64, vb.pp("l0"))?, linear(64, CELLS, vb.pp("l1"))?);
    let mut opt = AdamW::new(vars.all_vars(), ParamsAdamW { lr: 2e-3, ..Default::default() })?;
    let xs = Tensor::from_vec(x.concat(), (x.len(), x[0].len()), &dev)?;
    let ys = Tensor::from_slice(cells, cells.len(), &dev)?;
    for _ in 0..epochs {
        let logits = l1.forward(&l0.forward(&xs)?.tanh()?)?;
        opt.backward_step(&cross_entropy(&logits, &ys)?)?;   // full batch for brevity; the crate mini-batches
    }
    Ok(vars)                                                  // → safetensors → MlpWeights
}

The heuristic is a closure, because that is all Chapter 6's astar ever asked of a heuristic. The ϵ\epsilon is a field that starts as None and is filled only by an audit — the type makes it impossible to quote a bound that was never measured.

crates/learn/src/heuristic.rs
use search::{astar, Trace};
use cspace::TorusGrid;                                // Ch. 4's raster, 8-connected with wraparound
use crate::{encode::{heuristic_features, EnvEncoding}, infer::MlpWeights};

/// Learned cost-to-go. ĥ = h_oct · exp(ŷ): at least the admissible baseline where ŷ ≥ 0.
pub struct HeuristicNet<'a> { net: &'a MlpWeights, env: &'a EnvEncoding, grid: &'a TorusGrid,
                              goal: usize, pub epsilon: Option<f64> }

impl HeuristicNet<'_> {
    pub fn h(&self, v: usize) -> f64 {
        let base = self.grid.octile(v, self.goal);
        if base == 0.0 { return 0.0; }
        let y = self.net.forward(&heuristic_features(self.env, self.grid, v, self.goal))[0];
        base * f64::from(y).clamp(-1.0, 3.0).exp()   // clamp: one wild guess cannot overflow
    }
}

/// Derivation 3's hypothesis, measured: max over every reachable cell of ĥ/h* − 1.
pub fn measure_epsilon(h: &HeuristicNet, h_star: &[f64]) -> (f64, usize) {
    h_star.iter().enumerate()
        .filter(|(_, hs)| hs.is_finite() && **hs > 0.0)
        .fold((0.0, 0), |(eps, over), (v, hs)| {
            let r = h.h(v) / hs;
            if r > 1.0 + 1e-9 { (eps.max(r - 1.0), over + 1) } else { (eps, over) }
        })
}

/// Ch. 6's A*, which reopens; the bound below is Derivation 3 and holds only where ε was audited.
pub fn a_star_eps(grid: &TorusGrid, s: usize, g: usize, h: &HeuristicNet) -> Option<(Trace<usize>, f64)> {
    let eps = h.epsilon.expect("measure ε before quoting a bound");
    let t = astar(grid, s, g, |v| h.h(v))?;
    let bound = (1.0 + eps) * grid.cost_to_go(g)[s];
    debug_assert!(t.cost <= bound + 1e-9, "Derivation 3 violated on an audited world");
    Some((t, bound))
}

pub struct Audit { pub ratios: Vec<f64>, pub violated: Vec<char>, pub inconsistent: Vec<char>,
                   pub epsilon: f64, pub bound: f64 }

/// §3(b): a chain v₀ → v₁ → … → goal with edge costs c; both heuristics are 0 at the goal.
pub fn admissibility_audit(labels: &[char], h_star: &[f64], h_hat: &[f64], c: &[f64]) -> Audit {
    let ratios: Vec<f64> = h_hat.iter().zip(h_star).map(|(a, b)| a / b).collect();
    let violated = labels.iter().zip(&ratios).filter(|(_, r)| **r > 1.0 + 1e-12).map(|(l, _)| *l).collect();
    let inconsistent = (0..labels.len())
        .filter(|&i| h_hat[i] > c[i] + h_hat.get(i + 1).copied().unwrap_or(0.0) + 1e-12)
        .map(|i| labels[i]).collect();
    let epsilon = (ratios.iter().copied().fold(f64::MIN, f64::max) - 1.0).max(0.0);
    Audit { ratios, violated, inconsistent, epsilon, bound: (1.0 + epsilon) * h_star[0] }
}

The neural planner holds its repair planner as a field, not an Option: Derivation 4 says the guarantee lives there, and a planner without one has only a success rate.

crates/learn/src/neural_planner.rs
use manifold::T2;
use rand::{rngs::SmallRng, Rng};
use sampling::{tree::{Extend, RrtConnect}, FreeSpace, Steer};
use crate::{encode::EnvEncoding, infer::MlpWeights, Q};

pub struct MpnetLike<E: Extend<T2>> { policy: MlpWeights, env: EnvEncoding, repair: E,
                                      eta: f64, max_hops: usize, local: usize, fallback: usize }

pub struct NeuralPlan { pub waypoints: Vec<Q>, pub hops_ok: Vec<bool>,
                        pub fell_back: bool, pub path: Option<Vec<Q>> }

impl<E: Extend<T2> + Clone> MpnetLike<E> {
    pub fn plan(&self, free: &dyn FreeSpace<T2>, s: &Q, g: &Q, rng: &mut SmallRng) -> NeuralPlan {
        // D22.1: ≤ max_hops of q + η·tanh(π_θ), re-proposed with jitter where the endpoint collides.
        let waypoints = self.propose(free, s, g, rng);
        let hops_ok: Vec<bool> = waypoints.windows(2)          // the verifier, one hop at a time
            .map(|w| self.repair.steer(&T2, free, &w[0], &w[1]).is_some()).collect();
        let mut out = vec![*s];
        let (mut i, mut failed) = (0, false);
        let last = waypoints.len() - 1;
        while i < last {
            if hops_ok[i] { out.push(waypoints[i + 1]); i += 1; continue; }
            let j = (i + 1..last).find(|&j| free.is_free(&waypoints[j])).unwrap_or(last);
            match self.connect(free, &waypoints[i], &waypoints[j], self.local, rng) {
                Some(bridge) => { out.extend_from_slice(&bridge[1..]); i = j; }
                None => { failed = true; break; }
            }
        }
        // Derivation 4: the last resort answers the *original* query, so its guarantee is ours.
        let (path, fell_back) = if failed { (self.connect(free, s, g, self.fallback, rng), true) }
                                else { (Some(out), false) };
        let path = path.filter(|p| p.windows(2).all(|w| self.repair.steer(&T2, free, &w[0], &w[1]).is_some()));
        NeuralPlan { waypoints, hops_ok, fell_back, path }
    }

    fn connect(&self, free: &dyn FreeSpace<T2>, a: &Q, b: &Q, iters: usize, rng: &mut SmallRng) -> Option<Vec<Q>> {
        RrtConnect::new(T2, a.clone(), b.clone(), self.repair.clone(), self.eta, rng.random()).merge(free, iters)
    }
}

The worked example prints the two micro-examples exactly — they are arithmetic, and the test asserts them — and then trains on randomized Workbenches.

crates/learn/examples/learned_sampler.rs
use learn::{admissibility_audit, data, CoarseHistogram};

fn main() -> anyhow::Result<()> {
    // §3(a): 200 raw draws on four quadrants of one Workbench's torus.
    let hat = CoarseHistogram([100u64, 60, 30, 10]);
    let target = [0.55, 0.30, 0.10, 0.05];
    println!("counts {:?}  KL(p_hat||u) = {:.4} nats ({:.3} bits)  KL(p*||p_hat) = {:.4}  KL(p*||u) = {:.4}",
             hat.0, hat.kl_to_uniform(), hat.kl_to_uniform() / std::f64::consts::LN_2,
             hat.kl_from(&target), learn::kl(&target, &[0.25; 4]));
    println!("p_lambda(0.1) = {:.3?}", hat.mixture(0.1));

    // §3(b): a →2→ b →2→ c →1→ goal.
    let a = admissibility_audit(&['a', 'b', 'c'], &[5.0, 3.0, 1.0], &[4.6, 3.4, 0.8], &[2.0, 2.0, 1.0]);
    println!("h* [5.0 3.0 1.0]  h_hat [4.6 3.4 0.8]  violated {:?}  inconsistent {:?}  epsilon {:.4}  bound {:.3}",
             a.violated, a.inconsistent, a.epsilon, a.bound);

    // The pipeline: 600 random Workbenches × 3 queries → paths → sampler, policy; Dijkstra → heuristic.
    let seed = std::env::args().skip_while(|a| a != "--seed").nth(1).map_or(Ok(7), |s| s.parse())?;
    let ds = data::generate_dataset(data::Generator::Blocks, 600, 3, seed);
    println!("{}/{} PRM queries solved · {} sampler targets · {} policy targets",
             ds.solved, ds.attempted, ds.sampler.len(), ds.policy.len());
    Ok(())
}
cargo run --release --example learned_sampler -p learn -- --seed 7
counts [100, 60, 30, 10]  KL(p_hat||u) = 0.2442 nats (0.352 bits)  KL(p*||p_hat) = 0.0119  KL(p*||u) = 0.3162
p_lambda(0.1) = [0.475, 0.295, 0.160, 0.070]
h* [5.0 3.0 1.0]  h_hat [4.6 3.4 0.8]  violated ['b']  inconsistent ['b']  epsilon 0.1333  bound 5.667
954/1800 PRM queries solved · 21508 sampler targets · 44553 policy targets

The first three lines are exact and are what the tests below assert. The last is the TypeScript port's run of the same pipeline (lib/learn/__train__.ts, seed 7), which the page's numbers come from; a Rust run reproduces the pattern, not the digits, because the random streams differ. As recorded there: the sampler's cross-entropy fell to 3.393 nats in 50 epochs (uniform: 4.970); generating the paths took 147 s and training the sampler 93 s — 241 s in all, the number the Board amortizes.

crates/learn/tests/micro.rs
use learn::*;

#[test]
fn kl_four_cell_matches_hand_computation() {
    let h = CoarseHistogram([100u64, 60, 30, 10]);
    assert!((h.kl_to_uniform() - 0.2442).abs() < 1e-4);
    let entropy = -h.probabilities().iter().map(|p| p * p.ln()).sum::<f64>();
    assert!((h.kl_to_uniform() - (4f64.ln() - entropy)).abs() < 1e-12);       // ln N − H(p̂)
    assert!((h.kl_from(&[0.55, 0.30, 0.10, 0.05]) - 0.0119).abs() < 1e-4);
}

#[test]
fn eps_admissibility_three_nodes() {
    let a = admissibility_audit(&['a', 'b', 'c'], &[5.0, 3.0, 1.0], &[4.6, 3.4, 0.8], &[2.0, 2.0, 1.0]);
    assert_eq!(a.violated, vec!['b']);
    assert_eq!(a.inconsistent, vec!['b']);
    assert!((a.epsilon - 0.13333).abs() < 1e-4 && (a.bound - 5.6667).abs() < 1e-3);
}

#[test]
fn mixture_never_below_lambda_uniform() {
    let h = CoarseHistogram([100u64, 60, 30, 10]);
    for lambda in [0.05, 0.1, 0.3] { assert!(h.mixture(lambda).iter().all(|&m| m >= lambda / 4.0 - 1e-12)); }
    assert!(safe_learned_sampler(LearnedSampler::uniform_stub(), 0.0).is_err());
}

The crate's fourth test, a_star_eps_cost_within_bound, runs Derivation 3 as a property on 500 seeded lattices with h^=h∗(1+0.3 r)\hat h = h^*(1 + 0.3\,r), r∼U[0,1]r \sim U[0, 1]. The web port's checks are the same tests in TypeScript — its property test saw a worst realized ratio of 1.0711 over 30 searches, with 13 reopenings — plus the mixture floor with 100,000 empirical draws, the ϵ\epsilon audit, the proposal-plus-verifier recheck, the toy diffuser, and the Board's invariants.

Putting it together: reading the board

The integration lab is the Honest Benchmark Board above, read the way Definition 22.6 says to: thirty solvable queries per suite, from the training generator (axis-aligned blocks, like the real Workbench's block, post and shelf) and the held-out one (thin bars at arbitrary angles), budgets from 100 to 12,800 collision checks, the same world, query and seed for every planner.

Success at budget. In distribution, at 100 checks, the learned sampler at λ=0.1\lambda = 0.1 solves 60% against uniform's 47%; RRT-Connect solves 57%. At 400 checks the learned sampler has fallen behind: 77% against uniform's 90%, the bridge sampler's 80% and RRT-Connect's 93%. That shape — ahead early, behind later — is Derivation 2's cap in action. When the proposal aims right, it hits the corridor sooner than uniform; when it aims wrong, only the λ\lambda floor keeps it searching, at up to ten times uniform's waiting time, and by 400 checks those queries are what the column counts. At 12,800 checks the learned and uniform rows both solve every query in both suites — Derivation 1, on the board.

The held-out suite. We expected the learned sampler to lose its edge on the other generator. At λ=0.1\lambda = 0.1 it did not lose more than it had: held-out at 100 checks it solves 63% against uniform's 60%, and at 400 checks both solve 90% (bridge 80%, RRT-Connect 100%). Single worlds show the reversal — w22.1's default held-out seed costs the learner more than twice uniform's checks — but over thirty queries the bars were not harder for it than the blocks. Thirty queries is a small suite, and "did not degrade on one other generator" is not "generalizes".

Path cost. The learned sampler's first paths are consistently shorter: 1.28 times the incumbent in distribution against uniform's 1.33, and 1.23 against 1.43 held-out. That is Derivation 2's aim paying off in a currency nobody optimized for — the targets were nodes on shortest roadmap paths, so the proposal concentrates where short paths go. RRT-Connect, at 1.07 in distribution, beats every PRM row on cost as well.

Wall-clock. Here the learned row loses outright. A query's forward pass costs about 0.10 ms, so inference is not what makes the learned PRM's mean wall-clock in distribution 12 ms against uniform's 10 ms; the difference is in the planning loop itself. Amortizing the 241 s of data generation and training over kk queries can only make that worse, and the break-even against uniform is never. The MPNet-style planner pays 1.1 ms of inference per query and 53 ms of wall-clock, most of it in repairs, and reaches 93% at the full budget in both suites; on the remaining queries the fallback either ran out of iterations or overran the check budget.

What a fair reading concludes. On Reach's two-dimensional torus, with these small networks, a learned proposal buys a better first path and a head start at the smallest budgets, and it costs wall-clock and four minutes of offline work before the first query. A hand-designed single-query planner, RRT-Connect, is better than all of it. None of that is a verdict on learning in planning — the literature's wins are in seven-dimensional arms and repeated queries on similar scenes, where uniform sampling is hopeless and a training run is amortized over many queries — but it is a verdict on this setting, and the board's purpose is that such verdicts are possible at all.

What is principled, and what is still alchemy

The chapter closes with a ledger, because the field needs one more than it needs another leaderboard.

Principled. Any completeness-preserving proposal keeps Choset's guarantee with its constant multiplied by λ\lambda (Derivation 1), and a constructor that refuses λ=0\lambda = 0 makes that a property of the code. Maximum-likelihood training of a sampler is minimization of the forward KL divergence from the path distribution (Derivation 2), so "the sampler is aimed well" has a definition and a number. A heuristic's overestimate bounds the path cost exactly (Derivation 3), provided ϵ\epsilon is measured, and provided one says aloud that a validation ϵ\epsilon is a guarantee on the validation worlds and an estimate elsewhere — on this chapter's worlds the held-out ϵ\epsilon happened to be smaller than the validation one, which is luck, not a law. A proposal behind a sound verifier and a fallback on the original query inherits the fallback's guarantee (Derivation 4). And a benchmark that includes inference time, training time and held-out worlds (Definition 22.6) can falsify a claim, which is the only kind of benchmark worth running.

Still alchemy in 2026. Generalization across world generators: our held-out suite is one other generator, and no theorem says what the next one does. Validation ϵ\epsilon as a deployment guarantee: it is not one. Neural planners' success rates without repair: an empirical rate on a distribution of worlds, and the check that runs the net alone saw 0 of 8 in one suite and 4 of 8 in the other. Diffusion guidance through a non-differentiable constraint: collision is a set, not a cost; guidance raised the toy's valid fraction from 58% to 83% and no setting makes it one. The circularity: the sampler is trained on paths from the very PRM it is benchmarked against, so at best it reproduces PRM-with-a-large-budget, faster — it cannot find a passage the teacher never found. And amortization: a training run is paid for only by enough queries on similar worlds, and on this board the break-even never arrives.

Chapter 23's planning stack will carry a "learned sampler" toggle in Reach's panel, wired through exactly the Hybrid constructor above, and cite this board in its retrospective. The sister book's Chapter 25 applies the same principled-or-alchemy discipline to learning inside an estimator, where the verifier is a likelihood rather than a collision checker and the inheritance argument has a different shape.

Exercises

  1. Foundation exerciseDifficulty 2 of 3λ through the whole of Chapter 11

    Carry λ\lambda through Chapter 11's (ϵ,α,β)(\epsilon, \alpha, \beta)-expansiveness proof (Theorem 7.4.2), not only through the tiling bound of Derivation 1. Every volume in that proof is a uniform measure of a reach set or a lookout; under pλp_\lambda each is bounded below by λ\lambda times itself. Name the constant of the theorem that absorbs the change. Then show that a proposal with p^(A)=0\hat p(A) = 0 on one tile AA makes the failure probability of Derivation 1 bounded below by a positive constant independent of nn.

    Chapter 11's ball-tiling example (path length 1.2, clearance 0.1, unit square) needs 892 uniform samples for an exponential-form failure bound of 1%. With the same tiles and a mixture at λ = 0.1, how many samples does the same bound require?

  2. Foundation exerciseDifficulty 2 of 3The additive bound, and A* without reopening

    Prove the additive form of Derivation 3: if h^≤h∗+δ\hat h \le h^* + \delta everywhere, A* with reopening returns C≤C∗+δC \le C^* + \delta. Then build a four-node graph — start, two intermediate nodes, goal — with an admissible but inconsistent heuristic on which A* without reopening returns a suboptimal path, and explain which step of the derivation breaks.

  3. Conceptual exerciseDifficulty 1 of 3Predict the held-out sampler, then verify
    Predict first

    In Learned Sampler vs Uniform, set λ to its minimum, 0.02, and switch to the held-out generator. Before pressing play: across the seeds the success-at-budget curve accumulates, what happens to the learned curve compared with uniform's at the largest budgets?

  4. Conceptual exerciseDifficulty 2 of 3The deflated heuristic
    Predict first

    In Heuristic Calibration, switch from the learned ĥ to ĥ / (1 + ε_val), with ε_val = 0.662 from the validation worlds. Compared with the admissible octile heuristic, the deflated one will expand…

  5. Practical exerciseDifficulty 2 of 3A learned sampler for Rusty

    Implement Sampler on Rusty's R2\mathbb{R}^2 configurations in the Apartment for a LearnedSampler conditioned on a 64×6464 \times 64 occupancy raster, trained on 500 randomized floorplans (move interior walls and doorways; keep the outer walls). Wrap it with safe_learned_sampler and add its row to the Board, with a held-out generator that moves doors to walls the training generator never opened. Does the held-out gap shrink or grow with the larger raster? Report success at three budgets and the break-even kk, and say which of the two you believe more.

  6. Practical exerciseDifficulty 3 of 3Stretch: a learned heuristic for hybrid A*

    Train a heuristic for Chapter 21's hybrid A* on the Lot, with labels from backward Dijkstra on the (x,y,θ)(x, y, \theta) lattice for Hitch. Measure ϵ\epsilon on held-out Lots (different parking-row layouts) with measure_epsilon, run the planner, and compare the realized cost ratio to the bound (1+ϵ)(1 + \epsilon) and to the classical max⁡(hRS,hholonomic)\max(h_{RS}, h_{holonomic}) — the Reeds–Shepp distance and the obstacle-aware 2-D distance. Which of the three heuristics expands the fewest nodes, and which would you ship?

References

  1. Choset, H., Lynch, K. M., Hutchinson, S., Kantor, G., Burgard, W., Kavraki, L. E., and Thrun, S. (2005) Principles of Robot Motion: Theory, Algorithms, and Implementations. MIT Press.link to Principles of Robot Motion: Theory, Algorithms, and Implementations (opens in a new tab)

    §7.1.3's hand-designed biased samplers (OBPRM, Gaussian, bridge) are this chapter's baselines; §7.4.3's abstract path tiling, whose framework covers sampling from arbitrary distributions, is the theorem Derivation 1 extends; Appendix H.2's optimistic heuristic is Definition 22.3 at ε = 0.

  2. Ichter, B., Harrison, J., and Pavone, M. (2018) Learning Sampling Distributions for Robot Motion Planning. IEEE International Conference on Robotics and Automation (ICRA).link to Learning Sampling Distributions for Robot Motion Planning (opens in a new tab)

    The conditional variational autoencoder as a learned sampling distribution, mixed with uniform samples — the design this chapter's sampler simplifies to a categorical on T².

  3. Qureshi, A. H., Simeonov, A., Bency, M. J., and Yip, M. C. (2019) Motion Planning Networks. IEEE International Conference on Robotics and Automation (ICRA).link to Motion Planning Networks (opens in a new tab)

    MPNet: a learned waypoint policy with lazy states contraction, neural re-planning and a classical fallback — the instance of Derivation 4.

  4. Bhardwaj, M., Choudhury, S., and Scherer, S. (2017) Learning Heuristic Search via Imitation. Conference on Robot Learning (CoRL).link to Learning Heuristic Search via Imitation (opens in a new tab)

    Learning which node to expand by imitating an oracle with full knowledge of the cost-to-go — the sequential-decision view of a learned heuristic.

  5. Janner, M., Du, Y., Tenenbaum, J. B., and Levine, S. (2022) Planning with Diffusion for Flexible Behavior Synthesis. International Conference on Machine Learning (ICML).link to Planning with Diffusion for Flexible Behavior Synthesis (opens in a new tab)

    Diffuser: trajectory-level denoising with inpainted start and goal and gradient guidance by a cost — the model Derivation 5 states and the toy of w22.4 shrinks to one dimension.

  6. Ho, J., Jain, A., and Abbeel, P. (2020) Denoising Diffusion Probabilistic Models. Advances in Neural Information Processing Systems (NeurIPS).link to Denoising Diffusion Probabilistic Models (opens in a new tab)

    The noise-prediction objective and the ancestral sampling step used in Derivation 5, with the variational bound it reweights.

  7. Kingma, D. P. and Welling, M. (2014) Auto-Encoding Variational Bayes. International Conference on Learning Representations (ICLR).link to Auto-Encoding Variational Bayes (opens in a new tab)

    The evidence lower bound and the reparameterized encoder behind the CVAE sampler of the collapsible note.

  8. Hart, P. E., Nilsson, N. J., and Raphael, B. (1968) A Formal Basis for the Heuristic Determination of Minimum Cost Paths. IEEE Transactions on Systems Science and Cybernetics 4(2), 100–107.doi:10.1109/TSSC.1968.300136 (opens in a new tab)

    A* and the admissibility theorem whose ε-relaxation is Derivation 3.

  9. Karaman, S. and Frazzoli, E. (2011) Sampling-based Algorithms for Optimal Motion Planning. International Journal of Robotics Research 30(7), 846–894.doi:10.1177/0278364911406761 (opens in a new tab)

    The asymptotic-optimality baseline (RRT*, PRM*) a learned proposal must not pretend to beat; its incumbent is the Rust harness's cost reference.