Conjectures.io

BlogCompetitions

Two weeks, 208 proved float kernels: what the FloatLib competition found

Every accepted kernel is proved equal to the IEEE specification on every input. Proved exact is not the same as fast in general, and the gap between the two is most of this story.

4 Nov 2026 · 12 min read

Faster on unseen inputs
11×
208
Proved kernel files
1.9×
Behind plain C

The final champion, #323, runs FloatLib's four kernels 11.4 times faster than the ones we shipped, on inputs it was never scored on. Each of the 208 distinct accepted files is proved bit-for-bit equal to the specification. Proved sum now runs within 1.9 times of a plain C loop.

From 14 to 28 October 2026, miners on the subnet competed to make IEEE-754 floating-point arithmetic in Lean faster without giving up a single bit of correctness. A submission was one Lean file with four binary32 kernels: add and mul on single floats, sum over a vector and bmm, a batched matrix multiply. Each came with a machine-checked proof that it returns exactly what FloatLib's specification returns, for every possible input. The validator checked the proof, timed the file against the reigning champion, and crowned it if it was at least 3% faster.

339
submissions from 26 operators (coldkeys)
326
passed the proof gate; 208 of them distinct files, the rest byte-identical resubmissions
38
takeovers, each one checked by a bootstrap over 25 paired timing rounds
184
of the 208 files run on the hardware FPU and prove that they are allowed to

We wanted to answer two questions at the end. How fast are the final kernels on inputs nobody was scored on? And how far is that from plain, unproved hardware floats?

What a submission had to prove

The proofs cover every input: NaNs, infinities, subnormals, signed zeros, every vector length and every matrix shape. sum adds from +0 in index order, and bmm accumulates acc + a·b in reduction order, so reordering the additions or fusing a multiply-add is not exact and not allowed. Files were capped at 256 kB, only Lean's three standard axioms were accepted, and escape hatches such as native_decide and implemented_by were banned.

A new entry took over when the ratio of its fastest rounds to the champion's was at most 0.970, and a bootstrap over 25 paired rounds agreed that its median round was faster. The last three champions shared the payout 60/30/10.

There were two benchmarks. Until the afternoon of 22 October, entries were timed on 12 fixed, public input classes: one sum length (1,000), one bmm shape (50×100×50), and not a single NaN, infinity or subnormal. From then on, every run drew about 200 cases from a private suite: ML-like values, sparse inputs, extremes, specials, subnormals, and wide ranges of lengths and shapes. Miners got a public twin of it to tune on.

One idea from the first day

Since Lean 4.34, Float32, the machine's own float type, has a logical model inside Lean. It compiles to the CPU's float instructions, but you can also prove theorems about it, and Lean's documentation invites float libraries to prove their operations equal to it.

Entry #9 did exactly that on 14 October at 15:50 UTC. It computes on the hardware, proves that whenever the hardware result is not NaN it equals the IEEE specification, and falls back to the slow proved software kernel otherwise. NaNs are the one place where the two can differ: Lean's model has one canonical NaN, and FloatLib's specification keeps NaN payloads. #9 took over seven minutes later. By the end, 184 of the 208 distinct files worked this way.

The idea is at its best in a loop, because a NaN anywhere in a sum carries through to the end.

Summing on the FPU, with a proof that it is allowed to

  1. 10.75
  2. 2-1.5
  3. 32.25
  4. 40.1
  5. 53
  6. 6-0.625
  7. 71.2
  8. 80.05
  9. 9-2
  10. 104.5
  11. 110.3
  12. 12-0.8

acc = 0

0 on hardware · 0 in software

  • Hardware Float32
  • NaN carried through
  • Proved software kernel
The sum adds from +0 in index order, exactly as the specification does. A NaN anywhere carries through to the end, so a result that is a number means every step matched, and the whole loop ran at hardware speed. Only a NaN result pays for the slow path.

bmm works the same way, one dot product per output: each runs in hardware in the specified order, and only an output that comes out NaN is recomputed step by step. Single add and mul check for NaN on every call.

Part of this is new. The FloatLib version we pinned already proves hardware bridges for add, sub and sqrt, for finite inputs only. The winning file does not use them. It proves its own add and mul bridges from Lean's model, and its mul bridge excludes only NaN operands, so infinities are covered. FloatLib has no mul bridge at all.

Two champions, both 8.7× on the board

To see what the scores meant, we re-ran every accepted file against the shipped kernels on a dev server, on one pinned core with two seeds, on several input sets. One of them, which we call unseen inputs below, was built for this review and never used for scoring. The shipped kernels timed against themselves stay within 0.5% on every set.

Two champions came out of that with the same score and very different kernels.

Two champions, both 8.7× on the board

1×2×3×5×8×12×14 Oct17 Oct20 Oct23 Oct26 Octprivate inputs#174 · 1.4×#323 · 11.4×
#174 · Last champion on the fixed benchmark, 21 Oct
8.7×board1.4×unseen
#323 · Final champion, 26 Oct
8.7×board11.4×unseen
  • What the board recorded
  • The same champion on unseen inputs
Each step is a takeover. The board's figure restarts on 22 October, when scoring moved to a private suite and speed-ups were re-based on it. Unseen inputs are a private set built for this review and never used for scoring.
Data
championsinceon the boardon unseen inputs
#114 Oct1.2×1.0×
#314 Oct1.4×1.1×
#414 Oct1.5×1.2×
#514 Oct1.8×1.4×
#814 Oct2.0×1.6×
#914 Oct2.4×2.0×
#1214 Oct2.6×1.8×
#1414 Oct2.8×1.9×
#1614 Oct3.3×2.1×
#1714 Oct3.4×2.2×
#1914 Oct3.6×2.4×
#2014 Oct3.7×2.4×
#2215 Oct4.1×3.5×
#2615 Oct4.8×3.5×
#3715 Oct5.1×3.0×
#5215 Oct5.5×2.5×
#6416 Oct5.7×2.5×
#6916 Oct6.0×2.5×
#7416 Oct6.3×2.5×
#8517 Oct6.7×2.6×
#8717 Oct7.0×2.6×
#9317 Oct7.2×2.2×
#14720 Oct7.5×2.3×
#15520 Oct7.8×2.3×
#16520 Oct8.3×2.3×
#17421 Oct8.7×1.4×
#26522 Oct2.2×2.6×
#26722 Oct3.0×3.5×
#26822 Oct3.9×5.3×
#26922 Oct4.5×6.8×
#27122 Oct4.9×7.2×
#27222 Oct5.1×7.0×
#27422 Oct6.4×8.6×
#27922 Oct6.8×9.4×
#28423 Oct7.6×9.8×
#28523 Oct8.1×10.2×
#30924 Oct8.4×10.9×
#32326 Oct8.7×11.4×

The board puts #174 (21 October) and #323 (26 October) side by side: 8.74× and 8.72× faster than the shipped kernels. On inputs neither of them was scored on, #174 is 1.4× and #323 is 11.4×. The difference between them is what the benchmark measured for the first eight days, and what changed when it stopped being fixed.

The first eight days rewarded learning the benchmark

The fixed benchmark never timed a NaN, an infinity, a subnormal, a second sum length or a second bmm shape. Entries learned exactly that.

On 14 October, the ranking by benchmark score and the ranking by speed on unseen inputs agreed almost perfectly, at a rank correlation of 0.97. A day later it was 0.61. By the end it was −0.16: a better score on the benchmark had become slightly worse than random at predicting speed on new inputs. The private suite, scored on the same files after the fact, never fell below 0.88.

Does the ranking predict speed on new inputs?

−0.250.000.250.500.751.0014 Oct18 Oct22 Oct26 Octprivate suite scores0.97−0.160.97
  • The fixed public benchmark
  • The private suite, drawn fresh every run
Rank correlation between each file's score and its speed on unseen inputs, over every distinct file submitted up to that day, all re-run after the fact. Below zero, a better score predicts a slightly slower kernel.
Data
dayfilesfixed benchmarkprivate drawn suite
14 Oct200.970.99
15 Oct600.610.97
16 Oct710.530.97
17 Oct840.460.96
18 Oct890.420.93
19 Oct990.290.89
20 Oct1080.150.89
21 Oct128−0.050.91
22 Oct150−0.070.94
23 Oct170−0.140.95
24 Oct179−0.160.96
25 Oct184−0.170.96
26 Oct201−0.170.97
27 Oct207−0.160.97
28 Oct208−0.160.97

What tuning to one shape looks like

#174 is a resubmission of #172, and the code shows where its 8.7× came from. Its sum takes the hardware path only when the vector length is a multiple of four. Its bmm takes a fast table path only when the output width is between 49 and 52. The benchmark's 1,000 and 50×100×50 pass both checks every time.

A fast path that only opens on the benchmark's shape

1×2×5×10×20×30×26106365971271292512552579991000100110214093n = 1000: 24×every other length: 1.4 to 2.8×
  • #174, last champion on the fixed benchmark
  • #323, final champion
Speed-up against the shipped kernels. #174 (and #172, the same file) runs sum on the hardware only when the length is a multiple of four. Before 22 October the only length ever timed was 1,000, the only multiple of four in this probe.
Data
length#174#323
21.37×9.3×
62.47×15.9×
102.65×18.7×
632.8×25×
652.79×25.2×
972.75×24.6×
1272.75×24.1×
1292.79×24.4×
2512.73×23.4×
2552.73×23.4×
2572.72×23.4×
9992.66×22.7×
100023.8×22.7×
10012.66×22.8×
10212.67×22.9×
40932.6×22.5×

At n = 1,000, #174's sum is 24× faster than the shipped kernels. At every other length we tried it is 1.4 to 2.8×. Its bmm is about 300× faster on the benchmark's shape and its close neighbours, and slower than the shipped kernels everywhere else: 272× slower at 1×1×257.

None of this breaks a proof. #174 is exactly right on every input. It is only fast on the inputs it was shown.

A week of progress that was not

On unseen inputs, no champion after the morning of 15 October beat #22 and #26, at 3.5× and 3.4×. The best of the later ones reached 3.0×, while the board climbed from 4.8× to 8.7×.

We replayed the competition on our noise-free re-timings of the private suite, with the same 3% rule. In that replay, nothing after 15 October takes over until #238 on 21 October, the best file of the first eight days on unseen inputs at 4.4×. In the real competition #238 was timed against the shape-tuned #174, measured 1.107, and was rejected.

0.97 → −0.16
rank agreement between the benchmark and unseen inputs, 14 → 28 October
272×
how much slower than the shipped kernels #174's bmm is at shape 1×1×257

The private suite worked

From 22 October the full entry was scored on inputs drawn fresh for every run, and the board started telling the truth again.

  • The ranking agreed with unseen inputs at 0.97 by the end.
  • 11 of the 12 champions after the switch are faster on unseen inputs than on the suite they were scored on. The exception, #265, was the first entry timed on the new suite, against #174's collapsing bmm.
  • None of them is slower than the shipped kernels on any input family. Five champions from the first eight days are: #93 by up to 2.4× on sparse mul, #147, #155 and #165 by 1.3× on sparse bmm, and #174 by up to 4× on bmm.

It is not a complete safety net. 43 of the 208 files are more than 5% slower than the shipped kernels on at least one family of unseen inputs, because the rules had no per-family do-no-harm check. The champions after the switch happened not to be among them.

How fast the final kernels are

Kernel by kernel, against the kernels we shipped

1×10×100×shippedadd3.2×1.9×5×mul2.3×1.9×2.6×sum31.3×1.7×23.9×bmm288.3×0.5×76.1×
  • #174 on the fixed benchmark
  • #174 on unseen inputs
  • #323 on unseen inputs
Geometric mean over input families. #174's 288× bmm becomes 0.5× off the benchmark; #323 is faster than the shipped kernels on every kernel and every input family.
Data
addmulsumbmm
#174 on the fixed benchmark3.2×2.3×31.3×288.3×
#174 on unseen inputs1.9×1.9×1.7×0.5×
#323 on unseen inputs5×2.6×23.9×76.1×

On unseen inputs, #323 is 76× faster than the shipped kernels on bmm, 24× on sum, 5.0× on add and 2.6× on mul. Across 16 vector lengths from 2 to 4,093 and 11 matrix shapes from 1×1×257 to 128×8×128, it is 9 to 25× faster on sum and 21 to 167× on bmm. Its weakest input family anywhere, mul on sparse inputs, is still 1.4× faster than what we shipped.

How far that is from plain hardware floats

Closer than we expected.

Time per step, from proved to plain hardware floats

1 ns3 ns10 ns30 ns100 nsadd35 ns9 ns0.59 nsmul26 ns8.6 ns0.57 nssum31 ns1.5 ns0.8 nsbmm57 ns2 ns0.64 ns
  • Shipped proved kernels
  • #323, proved
  • Plain C float loop, nothing proved
The two Lean rows come from the competition's validator; the C row from a laptop with the same evaluation order, no fast-math and no FMA contraction. Different machines, so read it in orders of magnitude.
Data
addmulsumbmm
Shipped proved kernels35 ns26 ns31 ns57 ns
#323, proved9 ns8.6 ns1.5 ns2 ns
Plain C float loop, nothing proved0.59 ns0.57 ns0.8 ns0.64 ns

Proved-exact sum and bmm went from 40 to 90 times slower than plain floats to about 2 to 3 times slower. Single add and mul are still about 15 times slower, because every call pays for the NaN check, the conversions and the call itself.

1.9×
#323's proved sum against a plain C loop; the shipped kernels were 39× behind
3.1×
#323's proved bmm against plain C; the shipped kernels were 89× behind

Timing noise decided many takeovers

The validator's own admission rule, replayed on the published timing rounds, reproduces all 38 takeovers exactly. That let us look inside them.

Sixteen entries were byte-identical to the champion they were timed against. Their measured ratios ranged from 0.934 to 1.044. Three landed under the 0.970 bar, and one of them passed the statistical test and became champion, by beating an exact copy of itself.

Sixteen files timed against an exact copy of themselves

takeover bar 0.970identical 1.0000.940.960.981.001.021.04#165 beat a copy of itself
  • Under the bar, passed the test, became champion
  • Under the bar, stopped by the statistical test
  • Above the bar
The headline ratio is entry time over champion time, from each side's fastest rounds; the bar for a takeover was 0.970. Identical code measured anywhere from 0.934 to 1.044.
Data
entryratiooutcome
#1650.934under the bar, passed the test, became champion
#1770.953under the bar, stopped by the statistical test
#1610.960under the bar, stopped by the statistical test
#1760.970above the bar
#1570.973above the bar
#1630.991above the bar
#1560.992above the bar
#1580.996above the bar
#1661.000above the bar
#1481.000above the bar
#1751.008above the bar
#1781.012above the bar
#1621.018above the bar
#1591.022above the bar
#1641.028above the bar
#1601.044above the bar

The weak point is the combination of the two checks. The 3% bar applied to a headline built from each side's fastest round, and one lucky round moves that by several percent. The bootstrap then only checked that the median round was faster at all, not that it was 3% faster. Twelve of the 38 takeovers were under 3% faster by their median round, and six were under 1%. Ten in a row, from 16 to 21 October, were under 3%.

How much faster each new champion really was

0%10%20%30%private inputs10 in a row under 3%97%3%#1#64#265#323
  • 3% or more by the median round
  • Under 3%
Median of the 25 paired timing rounds against the champion it replaced. Every takeover cleared the 3% headline bar; the grey ones were under 3% by their median round. #265's 97% is the old champion's bmm collapsing on the new inputs.
Data
championdatemedian gain
#114 Oct14.9%
#314 Oct15.7%
#414 Oct7.6%
#514 Oct17.4%
#814 Oct9.4%
#914 Oct12.9%
#1214 Oct9.6%
#1414 Oct9.1%
#1614 Oct12.2%
#1714 Oct5.5%
#1914 Oct6.0%
#2014 Oct4.0%
#2215 Oct4.8%
#2615 Oct17.7%
#3715 Oct4.2%
#5215 Oct3.0%
#6416 Oct0.8%
#6916 Oct0.1%
#7416 Oct0.9%
#8517 Oct1.9%
#8717 Oct1.1%
#9317 Oct1.2%
#14720 Oct1.0%
#15520 Oct0.1%
#16520 Oct0.0%
#17421 Oct0.6%
#26522 Oct96.8%
#26722 Oct24.4%
#26822 Oct24.7%
#26922 Oct14.8%
#27122 Oct7.3%
#27222 Oct4.1%
#27422 Oct20.4%
#27922 Oct6.4%
#28423 Oct9.4%
#28523 Oct2.6%
#30924 Oct8.8%
#32326 Oct1.2%

Resubmitting was a strategy

118 of the 326 accepted entries were byte-identical resubmissions, and four of them took over. One, #165, was a copy of the reigning champion submitted by a different operator, and it won the title from the original. From 22 October, a file identical up to comments and whitespace was refused before timing.

The final takeover got around that. #323 differs from an earlier entry, #320, only in the length of a literal array inside a constant that no kernel reads. #320 measured 0.978 and missed the bar; #323 measured 0.969 and became the final champion.

To be fair to #323, on unseen inputs it is slightly faster than the champion it replaced, 11.4× against 10.9×, so the final result stands. But the last step was won on the timing, not on the code.

12 of 38
takeovers under 3% faster by their median round; six under 1%
4
takeovers by a byte-identical resubmission, one of them a rival's copy of the champion

A small field and a full file

26 operators stood behind 339 hotkeys, and three of them made 273 of the 339 submissions. One operator produced 21 of the 38 takeovers, including all three that held the paid places at the end.

The size cap shaped the code more than any reviewer would have. 114 of the 208 files sit within 6 kB of the 256 kB limit. #323 is 255,734 bytes: 425 lemmas, about 81% proof by volume, and 224 single-character notations such as ♠ and ⓑ, declared to squeeze it under the cap. It is correct, and it is very hard to read.

What this is good for

  • Verified machine learning in Lean. TorchLean already lets you run a network on FloatLib's proved binary32, which is slow, or on Lean's native Float32, which is fast and has nothing proved about it. This competition shows a third option: hardware speed for tensor loops with the theorem attached. That is what verified inference, certificate checking and reproducible reference runs inside Lean need.
  • FloatLib itself. A mul bridge, and add and mul bridges that cover infinities, are missing from FloatLib today, and they are exactly what Lean's documentation asks float libraries to provide.
  • Not a BLAS replacement. The specification fixes the order of additions and forbids fused multiply-add, which rules out the SIMD and blocking that make native matrix multiply fast.
  • Not a proof about your CPU. The theorems are about Lean's model of Float32. Running them correctly assumes the compiled float instructions follow IEEE-754 binary32: round to nearest, no flush-to-zero, no excess precision. Every Lean program that uses Float32 already makes the same assumption.
  • Not usable as is. The ideas carry over. The files would need a readable rewrite before anyone could maintain them.

What came next

FloatLib was never meant to be the end goal. It was the first stage of a bigger competition, run from the same repository, and its first takeover came on 28 October, the same evening FloatLib's board was frozen.

Running now

TorchLean: the same deal, for whole networks

FloatLib was the first stage. Its successor times proved inference kernels on five model families, on an x86 and an ARM machine. Its first takeover came on the evening FloatLib's board was frozen, and on every board it was 8 to 22 times faster than the shipped reference.

12
kernels in one Lean file, from dot products to layer norm
6
boards: MLP, ResNet, MobileNet, normalized MLP, Mixer and overall
107
takeovers across the boards in the first six days

Carried over from FloatLib

  • FloatLib's arithmetic as the specification: exact dot products, addition, fused multiply-add, maximum and division
  • The same proof gate, paired timing rounds and statistical takeover rule
  • The winning idea, one precision up: binary32 inputs decoded into 64-bit hardware floats, where a product of two of them is exact
The TorchLean competition

What we changed because of FloatLib

Most of the lessons above are already live in TorchLean.

  • Fresh inputs every evaluation. Every evaluation draws a new seed: typical, large-scale and boundary workloads, odd sizes, wide-exponent and sparse values. There are no fixed shapes left to learn.
  • A do-no-harm rule. An entry cannot take over if any workload, input type or CPU is more than 10% slower than the champion's.
  • Medians, not lucky rounds. The score is the median over rounds, not the fastest round. The statistical test still only asks for some gain, and we would like it to ask for the full 3%.
  • Copies refused before they are timed. That still means the same file up to comments and whitespace. A copy with a changed constant would get through, and catching those is next.
  • Two CPU architectures, so tuning to one machine buys less.
  • Published sources. Once a champion no longer holds any board, its file is published.
  • A bigger size cap, soon. Support for 1 MB files has landed in the code but is not live yet. TorchLean's champions are already pressed against 256 kB, just like FloatLib's.

The proofs made every one of these 208 files exactly right on every input. The benchmark decided which of them were fast, and for the first eight days it mostly measured itself.

Open tasks

If you want to try one, the catalog is open.