Physarum
A slime mould routes the placement in the anneal's time, and its saturation at the pins is the first scalar on this corpus that is not the size prior. It is also, measured at every scale, unable to order one board's own anneals — and the reason turned out to be the labels rather than the instrument.
The model
soldermask/physarum.py is Tero, Kobayashi and Nakagaki's Physarum model on the same 32×32 grid the congestion maps are drawn on: a Poisson solve for the pressures, dD/dt = |Q| - D for the tubes, Tero's own half-step. One mould per net, fed at the pin nearest its middle (Sun's MS3-PO feeding), and the nets negotiate for room the way PathFinder's routers do — an edge has room for the tracks its boundary length over the pitch allows on the layers not under a pad in either cell, a net's own pads put back for that net, and a net whose tube would be one track past that sees the edge as (base + history) × present longer.
The Laplacian is block tridiagonal by rows and is eliminated for every net at once in numpy — no scipy, as everywhere else here. 0.6 s a placement, which is the anneal's own budget.
Since 2026-09-09 the same loop is also compiled — mould/, Rust, four nets abreast as SIMD lanes, a thread a window — and runs whenever it is built: the same numbers to 7e-12 over 131 judged placements, the coarse mould seven times sooner and the windows seventeen, 0.12 s a placement for the windows on ten threads. The numpy loop stays as the reference and the fallback, and every number on this page was measured with it; tools/mould_ab.py is the check that the two agree.
This is a deliberate departure from the multi-commodity Physarum of Bonifaci et al. (2022), which shares one tube across commodities and rewards it. Copper cannot be shared.
physarum.escape() is the same idea at pad resolution: 8 mm windows over the board at 0.25 mm a cell, every window holding pins of two or more nets, the nets in it grown with a net that leaves the window fed at its pins and drinking at the rim. A track wider than a cell is spread over the edges it covers and sees the least room over them, so a track one cell from a pad has no room for its clearance. Eight seconds a placement.
Between boards: the best estimator on this corpus
Measured by tools/physarum_ab.py on 989 annealed placements of 131 boards, eight seeds each, every one judged by both routers under one DRC, label = clean under either. Higher is worse.
| Reading | AUC |
|---|---|
| Saturation at the pins (coarse, 32×32) | 0.757 |
Pins with no way out (escape(), pad resolution) | 0.784 |
| The same mould with the negotiation off | 0.634 |
| The fold | 0.660 |
| RUDY's peak | 0.680 |
| Pad copper peak | 0.662 |
| Net count | 0.650 |
ρ +0.33 holding net, pin and footprint counts (p 1e-27) for the coarse reading, +0.38 holding size (p 1e-37) at pad resolution — so it is not the size prior. A leave-one-out logistic on the net count goes 0.638 → 0.773 with it.
Held out on boards no tuning touched — 2,000 boards from the bank, 8,000 placements routed under both routers on Azure for about $12 — the mould is stronger, not weaker: AUC 0.829 on 1,774 boards against 0.713 for the net count, 0.739 for the fold, 0.68 for RUDY's peak; ρ +0.42 holding net, pin and footprint counts (p 1e-82 with boards as the unit). Corrected 2026-09-09: a count of pads closer than a track plus two clearances reads 0.821 on the same placements, within noise of the windows ([−0.006, +0.022] over boards), and holding that count with size the windows keep ρ +0.105 where the coarse mould keeps +0.206 — the pad-resolution reading is mostly a pitch counter; the coarse negotiated mould is what reads past the geometry (tools/pitch_ab.py, and §4 of the paper).
Over the whole bank (42,334 placements of 10,895 boards) the coarse mould's 95th-percentile saturation is 0.785 against 0.698 for the net count, and a ten-fold logistic on the net count goes 0.698 → 0.806 → 0.819 with both mould readings → 0.821 with the fold, reaching 0.856 on the placements that also carry the pad-resolution windows.
The negotiation is what makes it, on boards nobody tuned on. The ablation had only ever been run on the 131 tuning boards; on the held-out 6,822 placements the coarse mould with every net grown alone scores 0.740 against 0.803 negotiated, so two thirds of its margin over the net count (0.713) is the nets competing for room rather than the trees themselves.
Within a board: null, and the ceiling explains it
No score orders a board's own anneals. Over the 42 boards with both verdicts among their own eight placements, saturation at pins is 0.52 within board (21 right to 16); the best of fifty-odd scores is 0.60 (p 0.21); RUDY, wirelength and the fold sit in the same band. At pad resolution it is 0.55 (22 right, 15 wrong, p 0.32) — no better. On 2,045 mixed boards from the whole bank it reaches 0.574 (1,106 right, 753 wrong, p 1e-15): six hundredths of AUC, real and measured to three decimals, and not a judge.
Then the ceiling, which is the finding:
- The two routers disagree on 279 of the 989 placements, and agree on only 52 % of the placements of the 42 boards with both verdicts.
- One router's verdict orders the other's within a board at AUC 0.48 — 14 right, 25 wrong.
A full router does not know which of a board's anneals the other will close. So no estimator that cannot see the router's search can. The strongest within-board signal anywhere is the A* router's own via count against the merged verdict, 0.60.
The jitter study says the same thing from the other side: two thirds of placements are stable under both routers, and of 88 boards only 10 hold both a stably-routed and a stably-failed anneal under freerouting. The within-board differences a judge could learn exist, but they are a small minority of a board's variation — which is the precise reason the within-board tests came back null.
The estimator's place is board difficulty. The way to lift the clean rate on anneals is the second router: 539 of the 989 closed by freerouting, 746 by either.
What it costs and what it adds
Moving the weights one at a time over the 989 tuning placements (tools/physarum_sweep.py, thirteen variants, refitting nothing): the optimum is flat. Every present cost and every non-zero history lands within ±0.015 of the shipped 0.757. Removing the history collapses the pin reading. Ten iterations read better than forty at a quarter of the cost. A 48×48 grid and Tero's rotating feed change a hundredth.
At pad resolution the negotiation stops mattering, held out: pins with no way out score 0.819 negotiated against 0.829 alone, +0.010 for alone with a paired bootstrap over 1,774 boards of [+0.004, +0.016], and the other four escape readings tie inside their intervals. Negotiated windows cost a median 6.4 s against 5.8. The reversal is real, small, and measured where it belongs.
Against a classical negotiated-congestion router in the mould's place — McMurchie and Ebeling's PathFinder on the mould's own grid, room model and prices (tools/pathfinder_ab.py) — the negotiation is the estimator and the dynamics are two or three hundredths on the tail.
Where the mould does see cost within a board is the router's effort rather than its verdict: against the A* router's via count over each board's own placements, ρ +0.34, +0.19 with wirelength held (p 2e-3). The fold gave +0.25 and +0.16, and each survives holding the other (+0.15 and +0.22, p 0.03 and 5e-4) — topology and room are different readings.
It is not in the build
Physarum is an instrument, not a stage. Nothing in workspace.realise() calls it; it exists to answer whether a reading of the placement can stand in for a router run, and the answer above is for board difficulty yes, for choosing between anneals no.
tests/test_physarum.py (38) holds the physics rather than the numbers: the solve against a dense one to 1e-10, one track across every cut between two pins, a through-hole pad with no room out of its cell and none charged to its own net, twenty nets through a wall of header pads with room once they negotiate and forty without, and a chip walled in by slot pads 0.3 mm off with no way out where the same chip in the open has one — a header's 0.84 mm pin gaps, tried first, fit a track, and the mould found that.