Skip to main content
Workbench
optimise

Routing Race

Your circuit, a real coupling map, two routers. One is the SABRE heuristic every transpiler ships. The other starts as that heuristic and trains, in your browser, on the two-qubit count and depth of the circuits it produces. Watch which wins on your circuit — and by how much.

Preset or your own

8-qubit Fourier transform: 28 controlled phases, every pair of qubits talks to every other.

8 logical qubits · 148 gates after expansion · 56 two-qubit gates · depth 54. Toffolis are expanded to six CX before routing; barriers are dropped.

Heuristic — SWAP traffic per edge

Learned — SWAP traffic per edge

Train the learned router to fill this in.

Filled discs hold a logical qubit in the initial layout; the number on an edge is how many SWAPs the router put there. IBM heavy-hex, 8 physical qubits.

Two-qubit gate count, depth and added SWAPs for the original circuit, the heuristic routing and the learned routing.
Circuit2Q gatesvs originalDepthSWAPs addedValid on map
Original56540no
Heuristic (SABRE)158+182%13634yes
Learned (REINFORCE)

A SWAP is counted as the three CXs it compiles to, in both the gate count and the depth. “Valid on map” means every two-qubit gate sits on an edge; the original is only valid when the map happens to contain it.

Simulates the routed circuit on random states and compares it with the original up to the reported layouts (up to 14 physical qubits).

Not trained yet.

Each dot is one sampled routing, scored as −(2Q gates + ½·depth) relative to the heuristic, so the dashed line at −1 is the heuristic and higher is better. The solid line is a 20-episode average. It moves slowly: the policy has eight weights and starts as the heuristic, and REINFORCE on a whole-circuit reward is a noisy signal. The best single sample usually pulls ahead long before the average does.

Heuristic

initial q0→7 q1→5 q2→0 q3→1 q4→3 q5→2 q6→6 q7→4

final q0→2 q1→3 q2→1 q3→0 q4→4 q5→5 q6→7 q7→6

OPENQASM 3.0;
include "stdgates.inc";

qubit[8] q;
bit[8] c;

h q[4];
p(pi/4) q[6];
p(pi/8) q[2];
p(pi/16) q[3];
p(0.09817477042468103) q[1];
p(0.04908738521234052) q[0];
p(0.02454369260617026) q[5];
p(0.01227184630308513) q[7];
p(pi/4) q[4];
cx q[6], q[4];
p(-pi/4) q[4];
cx q[6], q[4];
p(pi/8) q[4];
h q[6];
p(pi/4) q[6];
swap q[3], q[4];
cx q[2], q[3];
p(-pi/8) q[3];
cx q[2], q[3];
p(pi/16) q[3];
p(pi/4) q[2];
cx q[4], q[3];
p(-pi/16) q[3];
cx q[4], q[3];
p(0.09817477042468103) q[3];
p(pi/8) q[4];
swap q[2], q[3];
cx q[1], q[2];
p(-0.09817477042468103) q[2];
cx q[1], q[2];
p(0.04908738521234052) q[2];
p(pi/16) q[1];
swap q[1], q[2];
cx q[0], q[1];
p(-0.04908738521234052) q[1];
cx q[0], q[1];
p(0.02454369260617026) q[1];
p(0.09817477042468103) q[0];
swap q[0], q[5];
cx q[0], q[1];
p(-0.02454369260617026) q[1];
cx q[0], q[1];
p(0.01227184630308513) q[1];
p(0.04908738521234052) q[0];
swap q[5], q[7];
swap q[0], q[5];
cx q[0], q[1];
p(-0.01227184630308513) q[1];
cx q[0], q[1];
p(0.02454369260617026) q[0];
swap q[4], q[6];
cx q[3], q[4];
p(-pi/4) q[4];
cx q[3], q[4];
p(pi/8) q[4];
h q[3];
cx q[6], q[4];
p(pi/4) q[3];
p(-pi/8) q[4];
cx q[6], q[4];
p(pi/16) q[4];
p(pi/4) q[6];
swap q[3], q[4];
cx q[2], q[3];
cx q[6], q[4];
p(-pi/16) q[3];
p(-pi/4) q[4];
cx q[2], q[3];
cx q[6], q[4];
p(0.09817477042468103) q[3];
p(pi/8) q[2];
p(pi/8) q[4];
h q[6];
p(pi/4) q[6];
swap q[2], q[3];
cx q[3], q[4];
p(-pi/8) q[4];
cx q[3], q[4];
p(pi/16) q[4];
p(pi/4) q[3];
swap q[1], q[2];
swap q[0], q[1];
swap q[0], q[5];
cx q[7], q[5];
p(-0.09817477042468103) q[5];
cx q[7], q[5];
p(0.04908738521234052) q[5];
p(pi/16) q[7];
cx q[0], q[5];
p(-0.04908738521234052) q[5];
cx q[0], q[5];
p(0.02454369260617026) q[5];
p(0.09817477042468103) q[0];
swap q[3], q[4];
cx q[4], q[6];
p(-pi/4) q[6];
cx q[4], q[6];
p(pi/8) q[6];
h q[4];
p(pi/4) q[4];
swap q[2], q[3];
swap q[0], q[1];
cx q[0], q[5];
p(-0.02454369260617026) q[5];
cx q[0], q[5];
p(0.04908738521234052) q[0];
swap q[5], q[7];
swap q[1], q[2];
swap q[0], q[1];
cx q[5], q[0];
p(-pi/16) q[0];
cx q[5], q[0];
p(0.09817477042468103) q[0];
p(pi/8) q[5];
swap q[1], q[2];
cx q[1], q[0];
p(-0.09817477042468103) q[0];
cx q[1], q[0];
p(0.04908738521234052) q[0];
p(pi/16) q[1];
swap q[4], q[6];
swap q[3], q[4];
swap q[1], q[2];
cx q[1], q[0];
p(-0.04908738521234052) q[0];
cx q[1], q[0];
p(0.09817477042468103) q[1];
swap q[0], q[5];
swap q[2], q[3];
swap q[1], q[2];
cx q[0], q[1];
p(-pi/8) q[1];
cx q[0], q[1];
p(pi/16) q[1];
p(pi/4) q[0];
swap q[4], q[6];
swap q[1], q[2];
cx q[3], q[2];
p(-pi/16) q[2];
cx q[3], q[2];
p(0.09817477042468103) q[2];
p(pi/8) q[3];
cx q[1], q[2];
p(-0.09817477042468103) q[2];
cx q[1], q[2];
p(pi/16) q[1];
swap q[3], q[4];
swap q[2], q[3];
swap q[0], q[1];
cx q[1], q[2];
p(-pi/4) q[2];
cx q[1], q[2];
p(pi/8) q[2];
h q[1];
p(pi/4) q[1];
swap q[3], q[4];
cx q[3], q[2];
p(-pi/8) q[2];
cx q[3], q[2];
p(pi/16) q[2];
p(pi/4) q[3];
swap q[1], q[2];
cx q[0], q[1];
cx q[3], q[2];
p(-pi/16) q[1];
p(-pi/4) q[2];
cx q[0], q[1];
cx q[3], q[2];
p(pi/8) q[0];
p(pi/8) q[2];
h q[3];
p(pi/4) q[3];
swap q[0], q[1];
cx q[1], q[2];
p(-pi/8) q[2];
cx q[1], q[2];
p(pi/4) q[1];
swap q[1], q[2];
cx q[2], q[3];
p(-pi/4) q[3];
cx q[2], q[3];
h q[2];

c = measure q;

Learned

Not trained yet.

Physical qubit indices. The routed circuit equals the original when logical qubit q is loaded onto physical qubit initial[q] and read back from final[q]; the “Verify equivalence” button checks exactly that with the simulator.

What to do

Pick a preset or paste a circuit, pick a topology, and read the heuristic row: it is computed the moment the inputs change. Then train. The learned router starts from the heuristic’s own initial layout and its own swap ranking, so at episode zero the two are the same router; everything after that is what the policy learned, or what its sampling found.

Try GHZ-12 on the ring against the line, then QFT-8 on heavy-hex with and without spare qubits. Then paste something of yours. The QASM box accepts what the simulator’s export writes, and Qiskit’s.

What the two routers are

Heuristic. SABRE (Li, Ding and Xie, 2019), the default routing pass in Qiskit and elsewhere. Keep the gates whose dependencies are done in a front layer; run the ones that sit on an edge; when none does, score every swap that touches a front-layer qubit by how much it shortens the front layer plus half of how much it shortens the next twenty gates, with a penalty for moving a qubit that just moved, and take the best. The initial layout comes from routing the circuit forward, then backward, then starting from where that ended.

Learned. The same loop with the swap choice replaced by a softmax policy over eight features of each candidate swap — the two SABRE terms, how many front gates it unblocks, recency, whether it undoes the last swap, progress on the oldest blocked gate, the degree of the qubits involved, and whether it moves through an empty qubit. Its weights start as the SABRE ranking exactly. Each episode samples a complete routing, scores it as −(two-qubit gates + ½·depth) relative to the heuristic, and takes one REINFORCE step on the weights. The result shown is the trained policy’s greedy rollout or, when that is worse, the best routing any episode sampled.

What this is a reproduction of, and what it is not

IBM’s production AI transpiler passes report improvements of 24% and 36% over the default heuristic on circuits of 100+ qubits, using a reinforcement-learned routing policy. This page is that idea at toy scale: eight weights instead of a neural network, twelve qubits instead of a hundred, a few hundred episodes in your browser instead of days on a cluster. The numbers you get are yours to compare, not a benchmark of IBM’s tool.

Be honest with yourself about where a win came from. The status line says whether the learned routing is the trained policy’s own greedy choice or the best of its samples. On small circuits the best sample usually wins, because a random tie-break in SABRE is often the difference between 24 and 30 swaps and sampling explores those ties. That is also true at scale: the production passes are a search guided by a learned model, not a single rollout.

Not modelled: gate direction on an edge, per-edge error rates, gate cancellation after routing, commutation beyond “gates on different qubits commute”, and calibration data of any real device. The heavy-hex map is IBM’s lattice pattern at a chosen width, not the qubit numbering of any particular backend. SWAPs are always three CXs; a compiler that turns a swap with an idle |0⟩ qubit into a cheaper move would count differently.

Checked: every routed circuit is validated against the map, and the “Verify equivalence” button runs the same statevector comparison the tests in lib/quantum/routing.test.ts run, which also hold the heuristic to zero swaps on all-to-all and the heavy-hex distances to a hand-computed table.