mirror of
https://github.com/lightningnetwork/lnd.git
synced 2026-08-15 12:50:15 +02:00
In this commit, we add findings sections 15 and 16: the ceiling arm (meta_harness at ten times the budget converges to a lower shelf, closing both halves of the exp-018 question) and the lying-channel breed (the first evolved attribution-confidence machinery, the flattest degradation profile measured, and the attempt-cap subsidy that reframes the verdict). Timeline gains exp-022, the exp-023 spec, and exp-024; the challenger count moves to eight everywhere it appears; the live-run panel now states plainly that no optimizer is live and that the interval-router branch is unbenchmarked in this simulator.
577 lines
27 KiB
HTML
577 lines
27 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Evolving Lightning's next routing algorithm — lnd × GEPA</title>
|
||
<meta name="description" content="An LLM-driven evolutionary search over Lightning routing algorithms, run against lnd's real pathfinding stack in an in-process payment simulator. What it found, and how." />
|
||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||
<link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,300..600;1,6..72,300..500&family=Spline+Sans+Mono:wght@400;500&display=swap" rel="stylesheet" />
|
||
<link rel="stylesheet" href="style.css" />
|
||
</head>
|
||
<body>
|
||
|
||
<a class="skip" href="#main">Skip to content</a>
|
||
|
||
<header class="site-header">
|
||
<div class="shell">
|
||
<a class="wordmark" href="index.html">lnd<span class="x">×</span>GEPA</a>
|
||
<nav class="site-nav">
|
||
<a href="index.html" aria-current="page">overview</a>
|
||
<a href="findings.html">findings</a>
|
||
<a href="drift.html">drift</a>
|
||
<a href="#run">live run</a>
|
||
</nav>
|
||
</div>
|
||
</header>
|
||
|
||
<main id="main">
|
||
|
||
<!-- ================= MASTHEAD ================= -->
|
||
<div class="shell masthead">
|
||
<div class="eyebrow">
|
||
Research log<span class="sep">/</span>Lightning Network pathfinding<span class="sep">/</span>July 2026
|
||
</div>
|
||
<h1>Evolving Lightning's <em>next</em> routing algorithm</h1>
|
||
<div class="masthead-grid">
|
||
<p class="standfirst">
|
||
We pointed an LLM-driven evolutionary search at lnd's real pathfinding
|
||
stack, inside a simulator that hides channel liquidity the way the network
|
||
does. <strong>Tuning its parameters found nothing. Replacing the algorithm
|
||
found a lot.</strong>
|
||
</p>
|
||
<div class="glance">
|
||
<div class="g hi">
|
||
<span class="l">attempts per payment<br>mainnet, vs lnd's 19.8</span>
|
||
<span class="v">2.3</span>
|
||
</div>
|
||
<div class="g">
|
||
<span class="l">objective, mainnet<br>vs lnd's 0.694</span>
|
||
<span class="v">0.791</span>
|
||
</div>
|
||
<div class="g">
|
||
<span class="l">evolved lines of Go<br>from a 300-line seed</span>
|
||
<span class="v">1,525</span>
|
||
</div>
|
||
<div class="cap">best evolved routers (mx_c3/hb1) against lnd's production stack</div>
|
||
</div>
|
||
</div>
|
||
<div class="byline">
|
||
<span>method <b>GEPA reflective evolution</b></span>
|
||
<span>reflection LM <b>gpt-5.6-sol</b></span>
|
||
<span>evaluator <b>cmd/routesim</b></span>
|
||
<span>experiments <b>exp-001 … exp-024</b></span>
|
||
<span>champions <b>hb1 + mx_c3</b>, unchanged since exp-007 · nothing live</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ================= 01 · FINDINGS ================= -->
|
||
<section id="result">
|
||
<div class="shell">
|
||
<div class="sec-head">
|
||
<div class="sec-no">01</div>
|
||
<h2>What the search found</h2>
|
||
<p class="sec-sub">
|
||
Every number below is held out. The closing check ran on a real mainnet
|
||
graph snapshot — lnd's home turf, and a topology the evolved routers had
|
||
never seen.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="keyrow wide">
|
||
<div class="key hi">
|
||
<span class="kn">8.6<span class="u">×</span></span>
|
||
<div class="kl">
|
||
fewer HTLC attempts per payment than lnd on the real mainnet graph, at
|
||
the same success rate, on a perfect failure channel
|
||
</div>
|
||
<div class="kf">2.3 vs 19.8 attempts · exp-009 · ratio retired by exp-019: under realistic attribution degradation the edge converts to success</div>
|
||
</div>
|
||
<div class="key">
|
||
<span class="kn">6<span class="u">of 6</span></span>
|
||
<div class="kl">
|
||
held-out tiers the evolved routers lead: sealed synthetic test,
|
||
out-of-distribution topologies, mainnet snapshot, drifting liquidity,
|
||
splitting pressure, and the atomic arena
|
||
</div>
|
||
<div class="kf">eight challengers have since failed to displace them</div>
|
||
</div>
|
||
<div class="key">
|
||
<span class="kn">0</span>
|
||
<div class="kl">
|
||
parameter settings, out of 400 evaluations of estimator and cost
|
||
knobs, that beat the lnd defaults
|
||
</div>
|
||
<div class="kf">exp-002 · the negative result that redirected the project</div>
|
||
</div>
|
||
</div>
|
||
|
||
<figure>
|
||
<div class="fig-head">
|
||
<span class="fig-t">Attempts per payment on a mainnet snapshot</span>
|
||
<span class="fig-n">Fig. 1 · lower is better</span>
|
||
</div>
|
||
<div class="plot resp" id="fig-attempts"></div>
|
||
<figcaption>
|
||
12,161 nodes, 39,659 channels, 100 payments from the network's
|
||
highest-degree node. Success rates are close — lnd 0.790, seed 0.820,
|
||
evolved 0.810 — so the whole difference lands in <b>how much probing it
|
||
takes to get there</b>. This is the tier lnd should win: its defaults were
|
||
tuned for this graph, while the evolved routers were bred entirely on
|
||
synthetic topologies. The attempt ratio is a perfect-channel figure;
|
||
exp-019 retired it, and under a degraded failure channel the same edge
|
||
shows up as success instead
|
||
(<a class="link" href="findings.html#attribution">the ladder</a>).
|
||
</figcaption>
|
||
</figure>
|
||
|
||
<figure>
|
||
<div class="fig-head">
|
||
<span class="fig-t">Composite objective, by router and held-out tier</span>
|
||
<span class="fig-n">Fig. 2 · higher is better</span>
|
||
</div>
|
||
<div class="plot resp" id="fig-champions"></div>
|
||
<div class="legend">
|
||
<span class="item"><i style="background:#a83f22"></i> evolved by GEPA</span>
|
||
<span class="item"><i style="background:#8a8175"></i> baseline (lnd, or hand-written)</span>
|
||
</div>
|
||
<figcaption>
|
||
Objective is <span class="mono">success − 0.01·min(extra attempts, 15) −
|
||
0.00002·min(fee ppm, 5000)</span>. The two evolved routers lead on all
|
||
three tiers. On synthetic corpora the gap is mostly success rate; on the
|
||
real graph it is almost entirely efficiency. Reruns are bit-identical
|
||
(stdev 0.00000 over five repeats), and both champions were audited clean
|
||
of simulator exploits.
|
||
</figcaption>
|
||
<details class="tableview">
|
||
<summary>Table view</summary>
|
||
<div class="tw">
|
||
<table class="data">
|
||
<caption>Composite objective. Combined is the mean of the two synthetic held-out sets.</caption>
|
||
<thead>
|
||
<tr>
|
||
<th>router</th>
|
||
<th class="num">mainnet</th>
|
||
<th class="num">hard sealed test</th>
|
||
<th class="num">out-of-distribution</th>
|
||
<th class="num">combined</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>lnd production stack<span class="sub">Dijkstra + mission control</span></td>
|
||
<td class="num" data-l="mainnet">0.694</td><td class="num" data-l="hard sealed test">0.309</td>
|
||
<td class="num" data-l="out-of-distribution">0.357</td><td class="num" data-l="combined">0.333</td>
|
||
</tr>
|
||
<tr>
|
||
<td>hand-written seed<span class="sub">~300 lines, cheapest path + blacklist</span></td>
|
||
<td class="num" data-l="mainnet">0.762</td><td class="num" data-l="hard sealed test">0.530</td>
|
||
<td class="num" data-l="out-of-distribution">0.487</td><td class="num" data-l="combined">0.509</td>
|
||
</tr>
|
||
<tr>
|
||
<td>hb1<span class="sub">evolved, 872 lines</span></td>
|
||
<td class="num" data-l="mainnet">0.790</td><td class="num" data-l="hard sealed test">0.586</td>
|
||
<td class="num" data-l="out-of-distribution">0.545</td><td class="num" data-l="combined">0.565</td>
|
||
</tr>
|
||
<tr class="best">
|
||
<td>mx_c3<span class="sub">evolved, 1,525 lines</span></td>
|
||
<td class="num" data-l="mainnet">0.791</td><td class="num" data-l="hard sealed test">0.583</td>
|
||
<td class="num" data-l="out-of-distribution">0.581</td><td class="num" data-l="combined">0.582</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</details>
|
||
</figure>
|
||
|
||
<div class="prose">
|
||
<p>
|
||
The headline is not the size of the win, it is <em>where the win came
|
||
from</em>. A 400-evaluation search over lnd's own pathfinding knobs — which
|
||
probability estimator, how much a failed attempt should virtually cost, the
|
||
floor on acceptable route probability — produced no setting that beat the
|
||
shipped defaults on validation. The defaults are locally robust; the
|
||
headroom is not in the knobs.
|
||
</p>
|
||
<p>
|
||
Meanwhile a deliberately naive 300-line router with a <em>different
|
||
paradigm</em> beat lnd's full production stack by 39% on the objective,
|
||
winning or tying every example. Handing that seed to the optimizer, and
|
||
letting it rewrite the whole algorithm rather than adjust its settings,
|
||
produced routers that beat both. What they invented — liquidity intervals
|
||
instead of decaying penalties — is the interesting part.
|
||
</p>
|
||
<p>
|
||
<a class="link" href="findings.html">Read the full findings →</a>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ================= 02 · METHOD ================= -->
|
||
<section id="method">
|
||
<div class="shell">
|
||
<div class="sec-head">
|
||
<div class="sec-no">02</div>
|
||
<h2>How the search works</h2>
|
||
<p class="sec-sub">
|
||
A candidate is a whole routing algorithm in Go. Every proposal is
|
||
compiled into a simulator and made to actually pay.
|
||
</p>
|
||
</div>
|
||
|
||
<figure>
|
||
<div class="fig-head">
|
||
<span class="fig-t">One turn of the evolutionary loop</span>
|
||
<span class="fig-n">Fig. 3</span>
|
||
</div>
|
||
<div class="schema">
|
||
<svg viewBox="0 0 1000 252" role="img" aria-label="The GEPA loop: propose, compile, simulate, score, reflect, then back to propose.">
|
||
<!-- stage boxes -->
|
||
<g>
|
||
<rect x="1" y="26" width="170" height="92" fill="#fbfaf6" stroke="#cbc4b2" stroke-width="1"/>
|
||
<text class="s-num" x="14" y="46">01</text>
|
||
<text class="s-title" x="14" y="66" font-size="14">propose</text>
|
||
<text class="s-body" x="14" y="86">the reflection LM</text>
|
||
<text class="s-body" x="14" y="102">rewrites the router</text>
|
||
</g>
|
||
<path d="M 179 72 h 20 m -6 -5 l 6 5 l -6 5" fill="none" stroke="#b3ab97" stroke-width="1.2"/>
|
||
<g>
|
||
<rect x="207" y="26" width="170" height="92" fill="#fbfaf6" stroke="#cbc4b2" stroke-width="1"/>
|
||
<text class="s-num" x="220" y="46">02</text>
|
||
<text class="s-title" x="220" y="66" font-size="14">compile</text>
|
||
<text class="s-body" x="220" y="86">go build −overlay drops</text>
|
||
<text class="s-body" x="220" y="102">it into the simulator</text>
|
||
</g>
|
||
<path d="M 385 72 h 20 m -6 -5 l 6 5 l -6 5" fill="none" stroke="#b3ab97" stroke-width="1.2"/>
|
||
<g>
|
||
<rect x="413" y="26" width="170" height="92" fill="#fbfaf6" stroke="#cbc4b2" stroke-width="1"/>
|
||
<text class="s-num" x="426" y="46">03</text>
|
||
<text class="s-title" x="426" y="66" font-size="14">simulate</text>
|
||
<text class="s-body" x="426" y="86">it pays, against hidden</text>
|
||
<text class="s-body" x="426" y="102">channel liquidity</text>
|
||
</g>
|
||
<path d="M 591 72 h 20 m -6 -5 l 6 5 l -6 5" fill="none" stroke="#b3ab97" stroke-width="1.2"/>
|
||
<g>
|
||
<rect x="619" y="26" width="170" height="92" fill="#fbfaf6" stroke="#cbc4b2" stroke-width="1"/>
|
||
<text class="s-num" x="632" y="46">04</text>
|
||
<text class="s-title" x="632" y="66" font-size="14">score</text>
|
||
<text class="s-body" x="632" y="86">success, minus attempt</text>
|
||
<text class="s-body" x="632" y="102">and fee penalties</text>
|
||
</g>
|
||
<path d="M 797 72 h 20 m -6 -5 l 6 5 l -6 5" fill="none" stroke="#b3ab97" stroke-width="1.2"/>
|
||
<g>
|
||
<rect x="825" y="26" width="170" height="92" fill="#fbfaf6" stroke="#cbc4b2" stroke-width="1"/>
|
||
<text class="s-num" x="838" y="46">05</text>
|
||
<text class="s-title" x="838" y="66" font-size="14">reflect</text>
|
||
<text class="s-body" x="838" y="86">keep it only if it holds</text>
|
||
<text class="s-body" x="838" y="102">up across the splits</text>
|
||
</g>
|
||
|
||
<!-- feedback return path -->
|
||
<path d="M 910 118 V 176 H 86 V 124 m -5 6 l 5 -6 l 5 6"
|
||
fill="none" stroke="#a83f22" stroke-width="1.4"/>
|
||
<text class="s-label-strong" x="498" y="199" text-anchor="middle" fill="#a83f22">
|
||
per-attempt failure traces are the feedback
|
||
</text>
|
||
<text class="s-body" x="498" y="220" text-anchor="middle">
|
||
which hop died, at what amount, with which BOLT error — so the next mutation is informed, not random
|
||
</text>
|
||
</svg>
|
||
</div>
|
||
<div class="scrollhint">scroll the diagram sideways →</div>
|
||
<figcaption>
|
||
The optimizer never sees hidden balances. A candidate gets the public
|
||
gossip graph, its own channel balances, and the errors its attempts
|
||
provoke — the same information a real Lightning sender has.
|
||
</figcaption>
|
||
</figure>
|
||
|
||
<div class="prose">
|
||
<h3>The simulator runs lnd's real code</h3>
|
||
<p>
|
||
<code>routing/sim_graph.go</code> is an in-memory Lightning network with
|
||
hidden per-channel balances and per-direction policies: base and
|
||
proportional fees, CLTV deltas, min and max HTLC. It implements lnd's own
|
||
<code>Graph</code> interface, so lnd's production pathfinding and mission
|
||
control run against it unmodified — which is what makes the baseline
|
||
honest.
|
||
</p>
|
||
<p>
|
||
Forwarding enforces real BOLT semantics. An attempt that asks too little
|
||
fee gets <code>FeeInsufficient</code>; one that asks for more than a hop
|
||
holds gets <code>TemporaryChannelFailure</code>, the liquidity miss that
|
||
all of routing is really about. Balances move on success and unwind on
|
||
failure. Topologies are synthetic (line, grid, hub-and-spoke, small-world,
|
||
scale-free) or loaded from a mainnet <code>describegraph</code> snapshot,
|
||
and hidden balances follow a half, uniform, or <em>bimodal</em> model — the
|
||
last one hugging one end of each channel, which is both realistic and the
|
||
hard case.
|
||
</p>
|
||
<p>
|
||
It is fast enough to be an inner loop: ten payments on a 200-node network
|
||
in about 0.3 seconds including process startup, so thousands of
|
||
evaluations are cheap.
|
||
</p>
|
||
|
||
<h3>A candidate is an algorithm, not a config</h3>
|
||
<p>
|
||
The evolvable unit is a Go file behind a paradigm-free interface: gossip
|
||
view, local balances, and per-attempt feedback in; a route out. GEPA
|
||
rewrites that file, <code>go build -overlay</code> compiles the rewrite into
|
||
the simulator, and the evaluator scores it on a corpus split into train,
|
||
validation and sealed test. Nothing in the contract mentions Dijkstra,
|
||
mission control, or probability estimators. Those become background
|
||
knowledge the reflection prompt may draw on, keep, reshape, or discard.
|
||
</p>
|
||
|
||
<div class="note">
|
||
<h4>the sandbox held</h4>
|
||
<p>
|
||
An independent audit found that the graph session handed candidates the
|
||
concrete simulator graph, type-assertable back into a read of hidden
|
||
balances or an outright rewrite of ground-truth liquidity — a
|
||
perfect-score reward hack using no banned identifiers. It was sealed the
|
||
same day, and every in-flight candidate was checked against it: zero
|
||
hits. No result on this site was produced through that hole.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ================= 03 · CORPUS ================= -->
|
||
<section id="corpus">
|
||
<div class="shell">
|
||
<div class="sec-head">
|
||
<div class="sec-no">03</div>
|
||
<h2>The scenarios it is scored against</h2>
|
||
<p class="sec-sub">
|
||
<span class="mono" id="c-files">—</span> scenario files,
|
||
<span class="mono" id="c-scen">—</span> payments,
|
||
split <span class="mono" id="c-split">—</span>. Validation picks the
|
||
incumbent; the test split stays sealed until a champion is declared.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="figgrid">
|
||
<figure>
|
||
<div class="fig-head">
|
||
<span class="fig-t">Topology</span>
|
||
<span class="fig-n">files</span>
|
||
</div>
|
||
<div class="bars" id="bars-topology"></div>
|
||
</figure>
|
||
<figure>
|
||
<div class="fig-head">
|
||
<span class="fig-t">Hidden liquidity model</span>
|
||
<span class="fig-n">files</span>
|
||
</div>
|
||
<div class="bars" id="bars-liquidity"></div>
|
||
<figcaption>
|
||
Bimodal is the hard regime: funds parked at one end of each channel,
|
||
so capacity tells you almost nothing about what will actually pass.
|
||
</figcaption>
|
||
</figure>
|
||
<figure>
|
||
<div class="fig-head">
|
||
<span class="fig-t">Payment amount</span>
|
||
<span class="fig-n">scenarios, satoshis</span>
|
||
</div>
|
||
<div class="bars" id="bars-amount"></div>
|
||
</figure>
|
||
<figure>
|
||
<div class="fig-head">
|
||
<span class="fig-t">Shard budget</span>
|
||
<span class="fig-n">scenarios, max parts</span>
|
||
</div>
|
||
<div class="bars" id="bars-parts"></div>
|
||
</figure>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ================= 04 · LIVE RUN ================= -->
|
||
<section id="run">
|
||
<div class="shell">
|
||
<div class="sec-head">
|
||
<div class="sec-no">04</div>
|
||
<h2>The last completed run: the engine adjudication (gepa arm)</h2>
|
||
<p class="sec-sub">
|
||
Telemetry from the optimizer, exported straight out of the GEPA run
|
||
directory. <span class="mono">code_drift1</span> has since finished all 400
|
||
evaluations, and so have exp-010's three splitting arms — all of them beaten
|
||
by the champions
|
||
(<a class="link" href="findings.html#splitting">the verdict</a>). exp-010b's
|
||
two atomic-arena arms have since closed the same way — the champion's fifth
|
||
hold, against an arena built to charge its probe ladder honestly
|
||
(<a class="link" href="findings.html#atomic">the atomic verdict</a>) — and so
|
||
has exp-012, which found no hot-cache regime anywhere but did find the one
|
||
change this work argues for upstream
|
||
(<a class="link" href="findings.html#coldcache">cold cache, hot load</a>).
|
||
Nothing is live now. The telemetry below is still
|
||
<span class="mono">exp-018</span>'s gepa arm, the engine adjudication, which
|
||
gave three optimizers the same seed, corpus and eval budget and found that
|
||
only gepa produced a router at all
|
||
(<a class="link" href="findings.html#omni">the adjudication</a>). Two
|
||
optimizer runs have finished since, both overnight and both closed:
|
||
<span class="mono">exp-022</span> bred a router against a channel that lies
|
||
about which hop failed
|
||
(<a class="link" href="findings.html#lying">the lying channel</a>), and
|
||
<span class="mono">exp-024</span> gave the runner-up engine ten times the
|
||
evaluations to see whether the band was ever budget starvation
|
||
(<a class="link" href="findings.html#ceilingarm">the ceiling arm</a>). In
|
||
between, exp-021 turned the findings into a flag-gated diff against lnd's own
|
||
stack, where one mechanism is now PR-ready and the other is a measured null
|
||
(<a class="link" href="findings.html#distillation">the distillation patch</a>).
|
||
</p>
|
||
</div>
|
||
|
||
<div class="note">
|
||
<h4>where the program stands</h4>
|
||
<p>
|
||
<strong>No optimizer run is live.</strong> Champions are unchanged at hb1
|
||
and mx_c3, eight challengers deep, and the arms still open are about
|
||
changing the environment rather than changing the search
|
||
(<a class="link" href="findings.html#ceilingarm">§15</a> is why).
|
||
</p>
|
||
<p>
|
||
<strong>exp-023, economic realism.</strong> The design spec is committed:
|
||
five mechanisms that put a price on things the arena currently gives away —
|
||
min and max HTLC pressure, inbound fees, fees as a first-class cost,
|
||
concurrent payments, latency — landing as five separately flag-gated stages
|
||
so that the flag-off byte-identity proof stays a sum rather than a product.
|
||
Stage A is in implementation. Nothing has been measured yet.
|
||
</p>
|
||
<p>
|
||
<strong>The interval-router branch.</strong> Separately from the simulator,
|
||
the champions' paradigm is being ported into lnd proper: fourteen commits on
|
||
<span class="mono">interval-router</span> that put a liquidity interval per
|
||
directed channel behind
|
||
<span class="mono">routerrpc.router=interval</span>, off by default, with
|
||
the stock stack untouched when the flag is unset. <strong>It has not been
|
||
benchmarked in this simulator.</strong> Nothing on this site is a
|
||
measurement of that branch, and no number here should be read as one.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="runbar">
|
||
<span><span class="dot"></span>run <b id="rb-run">—</b></span>
|
||
<span>reflection <b id="rb-lm">—</b></span>
|
||
<span>mode <b id="rb-mode">—</b></span>
|
||
<span>budget <b>150 evals</b></span>
|
||
</div>
|
||
|
||
<div class="statline wide" id="run-stats"></div>
|
||
|
||
<div class="prose" style="margin-bottom:34px">
|
||
<p>
|
||
<code>code_drift1</code> was the first run in a moving world. Its
|
||
predecessor, <code>code_gen2</code>, showed that more evaluations of a
|
||
static environment buy nothing — three independently bred routers landed
|
||
inside a band 0.014 wide (the
|
||
<a class="link" href="findings.html#ceiling">paradigm ceiling</a>). So this
|
||
run changed the environment instead of the budget: a virtual clock and
|
||
background senders moving hidden liquidity between our payments, and the
|
||
question of whether time-awareness re-evolves once knowledge genuinely goes
|
||
stale. <strong>It did — and it still lost.</strong> The winner stamps every
|
||
belief and halves its confidence every 35 virtual minutes, and the time-less
|
||
champions beat it on all four held-out tiers, drift included. The mechanism,
|
||
the numbers and the caveats are on the
|
||
<a class="link" href="drift.html">drift page</a>.
|
||
</p>
|
||
</div>
|
||
|
||
<figure class="wide" style="margin-top:0">
|
||
<div class="fig-head">
|
||
<span class="fig-t">Score over iterations</span>
|
||
<span class="fig-n">Fig. 4 · minibatch objective</span>
|
||
</div>
|
||
<div class="plot" id="score-chart"></div>
|
||
<div class="scrollhint">scroll the chart sideways →</div>
|
||
<div class="legend">
|
||
<span class="item"><i class="line" style="background:#a83f22"></i> best kept</span>
|
||
<span class="item"><i class="line" style="background:#2f6ea8"></i> proposed</span>
|
||
<span class="item"><i class="dash"></i> seed baseline</span>
|
||
</div>
|
||
<figcaption>
|
||
These are per-minibatch scores on the training split, not held-out
|
||
numbers — they are how GEPA decides what to keep, and they run
|
||
optimistically high. Every comparison in section 01 comes from separate
|
||
held-out runs instead.
|
||
</figcaption>
|
||
</figure>
|
||
|
||
<figure class="wide">
|
||
<div class="fig-head">
|
||
<span class="fig-t">Candidate lineage</span>
|
||
<span class="fig-n">Fig. 5 · select a candidate</span>
|
||
</div>
|
||
<div class="plot dag" id="lineage-dag"></div>
|
||
<figcaption>
|
||
Each node is one compiled, scored router. Accepted candidates stay on the
|
||
frontier and can be mutated further; rejects are dead ends. The scores are
|
||
per-minibatch and each is measured on a different subset, so a rejected
|
||
candidate can carry a higher number than the incumbent without being
|
||
better.
|
||
</figcaption>
|
||
</figure>
|
||
|
||
<div class="panels">
|
||
<div class="panel">
|
||
<div class="fig-head">
|
||
<span class="fig-t">Candidate detail</span>
|
||
<span class="fig-n" id="detail-sub">—</span>
|
||
</div>
|
||
<div id="cand-detail"></div>
|
||
</div>
|
||
<div class="panel">
|
||
<div class="fig-head">
|
||
<span class="fig-t">What the mutation changed</span>
|
||
<span class="fig-n">line diff</span>
|
||
</div>
|
||
<div class="selects">
|
||
<label>base <select id="diff-a"></select></label>
|
||
<span>→</span>
|
||
<label>candidate <select id="diff-b"></select></label>
|
||
</div>
|
||
<pre class="diff" id="diff-view"></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="shell">
|
||
<div class="readnext">
|
||
<div class="k">read next</div>
|
||
<a class="big" href="findings.html">Findings: what the evolved routers kept, dropped, and invented</a>
|
||
<p>
|
||
The definitive write-up — the paradigm result, the champion scores, an
|
||
anatomy of the evolved algorithms against lnd's, the ceiling three separate
|
||
lineages ran into, what happens when a router is handed knowledge it did not
|
||
pay for, the one patch that came out of all this and the theory that died
|
||
beside it
|
||
(<a class="link" href="findings.html#distillation">the distillation patch</a>),
|
||
what a second optimizer does with ten times the budget
|
||
(<a class="link" href="findings.html#ceilingarm">the ceiling arm</a>), what a
|
||
router bred against a channel that lies buys its robustness with
|
||
(<a class="link" href="findings.html#lying">the lying channel</a>), the
|
||
corrections we have had to make to our own record, and the process lessons
|
||
from twenty-four experiments.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
</main>
|
||
|
||
<div class="shell">
|
||
<footer class="site-footer">
|
||
<span>lnd × GEPA — Lightning routing evolution</span>
|
||
<span id="foot-run">run data loading…</span>
|
||
</footer>
|
||
</div>
|
||
|
||
<script src="app.js"></script>
|
||
</body>
|
||
</html>
|