<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Baris Bayrak Blog</title>
        <link>https://bayrak.dev/blog</link>
        <description>Baris Bayrak Blog</description>
        <lastBuildDate>Thu, 24 Sep 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[OpenJEV — One Refusal Test, an 80 MB File That Is Real, and the Option That Did Not Help]]></title>
            <link>https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test</link>
            <guid>https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test</guid>
            <pubDate>Thu, 24 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Ten open reimplementations of TypeSafe's Jev appeared in eleven days, and the interface is the easy part. We ran three of them — Needle 3 from Rust, Verdict through its ONNX export, and Supersonic Labs' Julia-1 on a CPU — against one real 11-tool surface and 24 off-topic controls. Choosing the right tool was close at 14, 15 and 10 of 24. Refusing an unusable request was not: 3, 16 and 0 of 24 — and with an explicit 'none of these' option offered, Julia-1 ignored it 24 times out of 24, which falsified the explanation this post first gave. Plus a 75.6 MB file named '8B' that is genuinely a head on a frozen 16 GB encoder. Updated: the field now has a public leaderboard — 140,178 requests, 0.59B to 26.09B models — we measured whether a Jev-style model can emit a word at all, and we tested a conformal-abstention claim that abstained on 22 of 24 requests on both sides of the test.]]></description>
            <content:encoded><![CDATA[<p>On 15 September 2026 TypeSafe released <strong>Jev</strong>, a model that takes a state and a set of typed questions and returns decisions with probabilities rather than text. Within eleven days there were at least ten open reimplementations of it, in five architecturally different ways, and one of them was trending on GitHub. That speed is itself the finding: <strong>"don't generate tokens, return probabilities" is an interface, and an interface takes an afternoon to copy.</strong> What does not copy is the part nobody puts in the launch video — whether the model can say <em>nothing here applies</em>.</p>
<p>We ran three of them against a real tool surface, 48 requests, one machine. Choosing the correct tool was close: <strong>14 of 24</strong> for a 121M on-device model, <strong>15 of 24</strong> for a 151M encoder-based one, <strong>10 of 24</strong> for a 144M multilingual one. Refusing a request that no tool could serve was not close at all: <strong>3 of 24</strong>, <strong>16 of 24</strong>, and <strong>0 of 24</strong>. And when we handed the third model an explicit "none of these" option, it ignored it on every single one of the 24. That last result cost us a claim we had already written down, which is the most useful thing in this post.</p>
<!-- -->
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-question-the-model-card-never-answers">The Question the Model Card Never Answers<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#the-question-the-model-card-never-answers" class="hash-link" aria-label="Direct link to The Question the Model Card Never Answers" title="Direct link to The Question the Model Card Never Answers" translate="no">​</a></h2>
<p>In an agent, a decision model sits in front of the tool list and answers "which of these applies?". The interesting case is not the request that matches a tool. It is the request that matches <em>none</em> of them, where the only correct output is an empty result.</p>
<p>That framing is inherited, and it is worth naming where from. A tool from <a class="" href="https://bayrak.dev/blog/claimaudit-three-ceilings-no-buyer">an earlier project on this site</a> audits technical claims against the hard ceiling of their own physics, and the single rule that made it work was <strong>keeping a control set of real products the tool must never flag</strong>. Without it, three separate domains shipped a confidently wrong bound. The same discipline applies here with the sign flipped: the control set is 24 requests that <em>no</em> tool in the set can serve, and the correct behaviour on every one of them is silence. A decision model that always returns an answer is not a router. It is a guessing machine with a probability attached.</p>
<p>So: 11 tools, 24 requests each way. The tools are the real ones registered in our own Rust agent library, read from the source rather than invented for the test:</p>
<table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>In scope — 24 requests, correct answer is a named tool</td><td>"show me the unstaged changes in this repo", "run cargo test", "search for TODO in the rust sources", "remember that the store is SQLite with FTS5", …</td></tr><tr><td>Off-topic — 24 requests, correct answer is <em>nothing</em></td><td>"what is the capital of France?", "give me a recipe for lentil soup", "play some jazz music", "write a haiku about the sea", …</td></tr></tbody></table>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-we-ran-and-one-control-first">What We Ran, and One Control First<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#what-we-ran-and-one-control-first" class="hash-link" aria-label="Direct link to What We Ran, and One Control First" title="Direct link to What We Ran, and One Control First" translate="no">​</a></h2>
<p><strong>Needle 3</strong> (Cactus Compute, 121M parameters, Apache-2.0) is the only one of the nine that ships a callable artifact: a static C engine plus a 2-bit container. We linked <code>libneedle.a</code> into a Rust binary, loaded the 35,335,380-byte weight file, and drove the C API directly. The whole runtime — weights, engine, tokenizer, config — is <strong>36.7 MB</strong>. No GPU, no Python, no network.</p>
<p><strong>Verdict / OpenJev</strong> (151M on a ModernBERT encoder, Apache-2.0) is a fine-tune of a GLiClass zero-shot classifier with an explicit abstention candidate in its schema. It exports to ONNX, so it is reachable from Rust, though we ran it through PyTorch. Its weights are <strong>605.5 MB</strong> in fp32 — 16× the whole Needle runtime, which is the price of the encoder design.</p>
<p>The first thing we did with Verdict was <strong>not</strong> run our test. It was to run theirs. An earlier pass of our own — 12 labels described as bare imperatives — produced a near-uniform distribution that looked exactly like a broken model, and it was actually a mis-shaped instrument: the engine renders every option as the literal sentence <code>It is {description}</code>, so "Run a program or shell command" was never a sentence fragment that template could use. Their README example caught it in one run: <strong>2 of 2</strong> correct on its home domain, and it abstains cleanly on an off-topic request. Only then was our test fair. We discarded that first pass rather than publish its number.</p>
<p><strong>Julia-1</strong> (Supersonic Labs, 2026-09-26) is the most carefully documented release in this set and the reason is worth stating plainly: it publishes its own <strong>failure</strong>. A 144.3M-parameter model built on <code>jhu-clsp/mmBERT-small</code>, Apache-2.0, trained to choose among 2–20 supplied answers, running on a CPU. It ships its evaluation as machine-readable metrics, a <code>provenance.json</code>, an <code>inference-policy.json</code>, and a reproduction script pinned to a dataset revision — and we verified the weights before using them: <code>model.safetensors</code> is 577,189,056 bytes and its SHA-256 is <code>df853bf7fe42…</code>, matching the hash in both their policy file and their reproduction script.</p>
<p>Two things in that paperwork made it the obvious third engine. It reports <strong>coverage</strong> alongside accuracy, and its Banking77 result lists <code>{"ok": 99, "abstained": 1}</code> — so this is a release that tracks abstention as a first-class outcome. And its <code>inference-policy.json</code> sets <code>"calibration": null</code>, meaning it explicitly does <strong>not</strong> claim calibrated probabilities. Both are rare. Its card states the weights occupy <strong>550.5 MiB</strong>, and they do: 577,189,056 bytes is 550.5 MiB exactly. Running it cost us an afternoon: 577 MB, no GPU, and the same PyTorch stack they used.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-result">The Result<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#the-result" class="hash-link" aria-label="Direct link to The Result" title="Direct link to The Result" translate="no">​</a></h2>
<table><thead><tr><th>24 off-topic inputs where no tool applies</th><th>Needle 3 (121M, on-device C)</th><th>Verdict (151M, encoder + abstain route)</th><th>Julia-1 (144M, mmBERT-small)</th></tr></thead><tbody><tr><td><strong>Refused</strong></td><td><strong>3 of 24</strong> — 12.5%</td><td><strong>16 of 24</strong> — 67%</td><td><strong>0 of 24</strong> — 0%</td></tr><tr><td>Picked the right tool</td><td><strong>14 of 24</strong></td><td><strong>15 of 24</strong></td><td><strong>10 of 24</strong></td></tr><tr><td>Mean confidence, wrong call</td><td><strong>0.622</strong></td><td><strong>0.283</strong></td><td><strong>0.648</strong></td></tr><tr><td>Mean confidence, right call</td><td><strong>0.860</strong></td><td><strong>0.497</strong></td><td><strong>0.839</strong></td></tr><tr><td>On disk</td><td>36.7 MB</td><td>605.5 MB</td><td>577.2 MB</td></tr><tr><td>Per-turn, this machine</td><td>57–460 ms</td><td>~1.4 s</td><td><strong>39–63 ms</strong></td></tr></tbody></table>
<p>The consequence for an agent is not symmetric. A wrong tool call is a bad turn. A router that answers <strong>21 of 24</strong> unusable requests is a system that cannot tell the user "I can't do that here" — and this is not a small model doing its best under a hard constraint, because the same model refuses correctly <em>some</em> of the time, which we will come to.</p>
<p>Verdict's failures are the mirror image, and it publishes them: <strong>four</strong> of its misses were <strong>false abstentions</strong> — it declined requests that <em>did</em> have a tool, including "build the project with cargo build" and "remember that the store is SQLite with FTS5". Its own documentation puts abstention recall at 97.5% on held-out in-domain data with precision down to 89.45% and 23 false abstentions. That is a calibration trade, and it is the right trade for a router: a false "I can't" costs one clarification, a confident wrong tool call costs the turn and possibly the session.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="confidence-is-not-a-refusal-signal">Confidence Is Not a Refusal Signal<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#confidence-is-not-a-refusal-signal" class="hash-link" aria-label="Direct link to Confidence Is Not a Refusal Signal" title="Direct link to Confidence Is Not a Refusal Signal" translate="no">​</a></h2>
<p>This is the part we did not expect, and it is the most useful thing in the whole exercise.</p>
<p>Needle's three refusals arrived at <strong>0.986</strong>, <strong>0.990</strong> and <strong>0.990</strong> confidence. Its <strong>most confident wrong call was 0.891</strong>. Whatever the refusal path is measuring, it is not the model's uncertainty: it declines when it is <em>certain</em> there is no tool, and calls when it is <em>uncertain but has a plausible match</em>. So <code>confidence</code> is not merely useless as a scope gate — in the tail it points the wrong way.</p>
<p>Concretely: a gate set at <strong>0.7</strong> would have acted on <strong>9 of 24</strong> requests that no tool could serve. Confidence does separate right from wrong <em>on average</em> — <strong>0.860</strong> for correct calls against <strong>0.622</strong> for wrong ones — and that separation reproduced exactly across two sample sizes. It is simply a different quantity from "is this request in scope", and the two get conflated constantly because both are reported as a number between 0 and 1.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="eleven-releases-five-families">Eleven Releases, Five Families<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#eleven-releases-five-families" class="hash-link" aria-label="Direct link to Eleven Releases, Five Families" title="Direct link to Eleven Releases, Five Families" translate="no">​</a></h2>
<p>The releases are not variations on one design. They are five different bets:</p>
<table><thead><tr><th>Family</th><th>Who</th><th>What it actually is</th></tr></thead><tbody><tr><td><strong>Read the answer scores off a stock LLM</strong></td><td>SemIf (MIT), Simple Jev (featherless-ai)</td><td>No new weights at all — intercept the model before it generates and read its option logits</td></tr><tr><td><strong>Train a dedicated non-generative model</strong></td><td>Laya, Von (395M), Verdict (151M), Nimble, <strong>Julia-1 (144M)</strong></td><td>Purpose-built encoders with decision heads; the honest projects publish calibration curves and failure slices</td></tr><tr><td><strong>Retrofit an LLM with a decision head</strong></td><td>Kev (Qwen3.5 0.8B–8B)</td><td>Keep the LLM, add the head, share one reading of the state across many questions</td></tr><tr><td><strong>Diffusion fill-in</strong></td><td>OpenJev on DiffusionGemma-26B</td><td>Blank the answer slots and fill them all in one shot, like inpainting</td></tr><tr><td><strong>Freeze the LLM, train contrastive heads</strong></td><td>CLM</td><td>The encoder is never touched; what you download is a head, not a model</td></tr><tr><td><em>Not a reimplementation</em></td><td>CUA-S1-FORMS (trycua)</td><td>Research code, <strong>no weights distributed</strong> — the one release here that cannot be run at all</td></tr></tbody></table>
<p>Only one of these families scores <strong>16 of 24</strong> on the refusal test, and it is the one whose schema carries an explicit <em>insufficient evidence</em> route. <strong>That correlation is real; the explanation we first gave for it was wrong, and the third engine is what showed us.</strong> We had written that a schema of five options makes "none of them" unrepresentable, "no matter how good the weights are". Julia-1 does not have that limitation — and it still refused nothing. Representation is necessary and nowhere near sufficient: a model refuses only if the option is there <strong>and</strong> it was trained to use it. Verdict's route is trained (its card names the objective, RLCD); the others have never seen an abstain option, so handing them one puts it out of distribution.</p>
<p>The most valuable sentence in any of the eleven releases is Verdict measuring itself against the thing it reimplements: on TypeSafe's public benchmark OpenJev scores <strong>48.07%</strong>, against <strong>90.80%</strong> for Jev and 88.43% for a 26B diffusion model. A small model honestly reporting that it is half as accurate as the model it copies — alongside a cardinality study showing its own top-1 accuracy falling from <strong>97%</strong> at three candidates to <strong>72%</strong> at 25 — is worth more than any launch chart. Julia-1's equivalent is a Banking77 pilot at <strong>64 of 100</strong> against Jev's 87%, published next to the three tasks where it wins.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-option-that-did-not-help">The Option That Did Not Help<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#the-option-that-did-not-help" class="hash-link" aria-label="Direct link to The Option That Did Not Help" title="Direct link to The Option That Did Not Help" translate="no">​</a></h2>
<p>This is the experiment we would run first, if we were starting again, because it converts a correlation into a cause. On the same 24 off-topic requests and the same 11 tools, we ran Julia-1 twice: once with the 11 tool options exactly as every other engine saw them, and once with a twelfth option described as "a request that none of these tools can serve".</p>
<table><thead><tr><th>Julia-1</th><th>options</th><th>Off-topic refusals</th><th>Tool correct</th><th>mean p, wrong / right</th></tr></thead><tbody><tr><td>A — tools only</td><td>11</td><td><strong>0 of 24</strong></td><td>10 of 24</td><td>0.648 / 0.839</td></tr><tr><td>B — tools + an offered abstain option</td><td>12</td><td><strong>0 of 24</strong></td><td>8 of 24</td><td>0.636 / 0.992</td></tr></tbody></table>
<p><strong>Offering the option changed nothing.</strong> On all 24 unusable requests it picked a tool anyway — 16 times <code>execute_command</code>, plus three <code>web_fetch</code>, three <code>read_file</code> and two <code>web_search</code> — and on "what is the capital of France?" it reached for a shell command. Adding the option did not merely fail to help; it also cost two points of tool accuracy, which is what you would expect if the extra label is noise to a model that has no idea what to do with it.</p>
<p>The conclusion is narrower than the one we published first, and more useful. A reimplementation can copy the interface in a day, but the interface is not what makes a decision model safe to put in front of a tool list. What makes it safe is a route that is present <strong>and trained</strong>, and that is a data and objective decision made long before anyone writes a launch post.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-80-mb-file-called-8b">The 80 MB File Called "8B"<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#the-80-mb-file-called-8b" class="hash-link" aria-label="Direct link to The 80 MB File Called &quot;8B&quot;" title="Direct link to The 80 MB File Called &quot;8B&quot;" translate="no">​</a></h2>
<p>CLM arrived with a claim that does not survive its own file listing: an <strong>"8B"</strong> model whose only weights file is <strong>75.6 MB</strong>. Eight billion parameters at fp16 is about 16 GB, so those numbers cannot describe the same object. They don't, and the model card says so plainly: CLM is "two small projection heads on top of a <strong>frozen Qwen3-8B encoder</strong>".</p>
<p>The arithmetic confirms it exactly. Each head is a projection <code>4096 → 1536 → 1536 → 512</code>, which is <code>4096×1536 + 1536 + 1536×1536 + 1536 + 2×1536 + 512×1536 + 512</code> = <strong>9,443,840</strong> parameters, so the pair is <strong>18,887,680</strong> in fp32 = <strong>75,550,720</strong> bytes against a file of <strong>75,557,149</strong>. The remaining <strong>6,429 bytes</strong> — 0.0085% — are torch archive overhead, not weights.</p>
<p>So the file is real, correctly described, and not the good news either. The base it requires, Qwen3-8B, is 8,190,726,144 parameters at bf16 = 16,381,452,288 bytes, which is <strong>216.8×</strong> the size of the artifact you download. Deploying CLM means serving an 8B encoder on a GPU with vLLM; the 19M of heads are a rounding error on top. It is the opposite of the on-device proposition, and it will never be small.</p>
<p>Two more caveats are in the card rather than the announcement. The headline agentic results — DeepSWE <strong>81.6%</strong>, Terminal-Bench 2.1 <strong>87.6%</strong> — come from <strong>fine-tuned heads in a separate repository</strong>, not from this checkpoint, which is the starting point for that fine-tune. And its "up to 9×" is the card's own "<strong>13× faster</strong> with <strong>~1k candidates</strong>", which is a caching result.</p>
<p>And the structural point that matters here: <strong>CLM cannot abstain at all.</strong> Searching its schema, server, engine, client and fine-tuning guide for any notion of abstention or refusal returns nothing. Its yes/no type is strictly <code>false</code>/<code>true</code>, and its choice type is a softmax over whatever candidates you supply — the card's own words are "<strong>no generation</strong>: CLM only scores the candidates you give it, and its probabilities are relative to that set". On our 24 controls it would return an answer every time, <strong>0 of 24 by construction</strong>, and adding a "none of the above" option is a modelling decision the user has to make because the API does not make it.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-idea-worth-stealing">The Idea Worth Stealing<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#the-idea-worth-stealing" class="hash-link" aria-label="Direct link to The Idea Worth Stealing" title="Direct link to The Idea Worth Stealing" translate="no">​</a></h2>
<p>CLM's speed claim is not about its architecture being clever. It is that <strong>states and actions are encoded separately</strong>, so the action side is computed once and reused while the state changes. For a fixed action set that is exactly the right decomposition — and it describes our problem precisely: an agent's tool list is fixed, and what changes per turn is the request. Encode the 11 tools once at startup, encode only the state per turn, score. That works with any scorer in the table above, needs no new model, and is the cheapest real improvement in this entire body of work.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="candidate-count-matters-and-not-monotonically">Candidate Count Matters, and Not Monotonically<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#candidate-count-matters-and-not-monotonically" class="hash-link" aria-label="Direct link to Candidate Count Matters, and Not Monotonically" title="Direct link to Candidate Count Matters, and Not Monotonically" translate="no">​</a></h2>
<p>One more measurement, because the candidate menu turns out to be a variable rather than a constant. Same 24 inputs at three menu sizes:</p>
<table><thead><tr><th>Verdict surface</th><th>labels</th><th>Off-topic refusals</th><th>Tool correct</th></tr></thead><tbody><tr><td>first 4 tools</td><td>5</td><td><strong>19 of 24</strong> (79%)</td><td>10 of 24</td></tr><tr><td>first 8 tools</td><td>9</td><td><strong>8 of 24</strong> (33%)</td><td>14 of 24</td></tr><tr><td>all 11 tools</td><td>12</td><td><strong>16 of 24</strong> (67%)</td><td>15 of 24</td></tr></tbody></table>
<p>Refusal falls hard from 5 labels to 9, then <em>rises</em> at 12. Three points at n = 24 cannot explain that, and we are not going to pretend otherwise — but it is enough to say the effect is real and not simply "more options, worse", which is consistent with Verdict's own note that its abstention "depends heavily on the seen prompt structure". Their published curve, on 100 samples per slice, is the version to trust: <strong>97% → 96% → 91% → 78% → 72%</strong> accuracy as the menu grows from 3 to 25. An 11-tool router is a 12-candidate problem, which is where their own data already says to be careful.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="a-month-later-the-field-has-a-leaderboard">A Month Later, the Field Has a Leaderboard<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#a-month-later-the-field-has-a-leaderboard" class="hash-link" aria-label="Direct link to A Month Later, the Field Has a Leaderboard" title="Direct link to A Month Later, the Field Has a Leaderboard" translate="no">​</a></h2>
<p>Everything above measured ten reimplementations, none larger than 151M parameters. That is no longer the shape of the field. <strong>Decision 3.0</strong>, released on 10 October by the vLLM Semantic Router team, is six Apache-2.0 decision models from <strong>0.59B to 26.09B</strong>, built on Qwen3.5 and Qwen3.8 backbones, <strong>multimodal</strong> (images and video), and published as <code>zero-shot-classification</code> rather than text generation. Its card states the design intent in one sentence: the model "returns a probability for every answer, <strong>without generating text</strong>."</p>
<p>So the interface we said takes an afternoon to copy is now a category label, and the category has a public number attached. The <strong>Jev Decision Index 0.3.1</strong> board runs on a suite of <strong>140,178 requests</strong>:</p>
<table><thead><tr><th>Model</th><th>Index</th><th>Public</th><th>Same-skill</th><th>New-domain</th></tr></thead><tbody><tr><td><strong>d3</strong> (26.09B)</td><td><strong>64.7</strong></td><td><strong>65.5</strong></td><td><strong>62.8</strong></td><td><strong>56.6</strong></td></tr><tr><td>Perplexity Decider v1.1 (27B)</td><td>62.8</td><td>62.3</td><td>61.1</td><td>55.6</td></tr><tr><td>Fastino GLiDE (28B, no-thinking)</td><td>60.2</td><td>59.1</td><td>59.5</td><td>52.9</td></tr><tr><td><strong>Jev</strong> — the model being reimplemented</td><td>60.1</td><td>58.0</td><td>58.0</td><td>55.0</td></tr><tr><td>Torchcast Decision 27B</td><td>59.9</td><td>65.1</td><td>58.1</td><td>50.8</td></tr></tbody></table>
<p>The reference implementation is now fourth on a board Perplexity and Fastino have entered. The column that should interest anyone building on this is <strong>New-domain</strong>, where even the leader reaches only <strong>56.6</strong> — that is the generalisation gap we could not measure with 24 hand-written controls and they now attempt with 140,178 requests. The size ladder tells its own story too: the same family scores <strong>28.82</strong> at 0.59B against <strong>65.45</strong> at 27B.</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-line-to-interrogate">The Line to Interrogate<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#the-line-to-interrogate" class="hash-link" aria-label="Direct link to The Line to Interrogate" title="Direct link to The Line to Interrogate" translate="no">​</a></h3>
<p>Both cards lead their highlights with the same sentence: <em>"all 140,178 public requests answered, <strong>none unsupported</strong>."</em></p>
<p>Read that against the test above. <strong>A decision model that never declines is a guessing machine with a probability attached</strong> — that is precisely what we measured, where three engines refused <strong>3, 16 and 0</strong> of 24 requests that no tool could serve. Whether "none unsupported" is an achievement or a confession turns on one question the card does not answer: <strong>does the 140,178-request suite contain cases where the correct answer is "unsupported"?</strong> If it does not, the index cannot measure the behaviour that separated the engines here, and the claim is unfalsifiable rather than good. That is the control-set rule from <a class="" href="https://bayrak.dev/blog/claimaudit-three-ceilings-no-buyer">the claimaudit post</a>, pointed at the industry's own benchmark — and it is the first thing we would check before trusting the board.</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="can-these-models-emit-a-word-at-all">Can These Models Emit a Word at All?<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#can-these-models-emit-a-word-at-all" class="hash-link" aria-label="Direct link to Can These Models Emit a Word at All?" title="Direct link to Can These Models Emit a Word at All?" translate="no">​</a></h3>
<p>We had asserted "no", so we measured it. The artifact settles half the question: a released Jev-style checkpoint carries <strong>no language head</strong>. Of the four pieces <code>ModernBertForMaskedLM</code> needs to produce a vocabulary distribution, <strong>three are absent</strong> — <code>head.dense</code>, <code>head.norm</code>, <code>decoder.bias</code> — leaving only the tied embedding table, which is a shared <em>input</em> tensor. Nothing in the file outputs a word distribution; the model returns one scalar per marked option.</p>
<p>The other half needed a control. We loaded the parent <code>jhu-clsp/mmBERT-small</code>, which <em>does</em> carry the full MLM head, then swapped in <strong>only</strong> the post-trained encoder — <strong>134 of 134</strong> tensors changed:</p>
<table><thead><tr><th>prompt</th><th>parent (control)</th><th>post-trained encoder</th></tr></thead><tbody><tr><td>"The guard lowered his <code>&lt;mask&gt;</code> and let the cart through."</td><td>horse .39 · head .18 · gun .09</td><td><strong>gun .48</strong> · pistol .23 · rifle .10</td></tr><tr><td>"…asked the traveller to <code>&lt;mask&gt;</code>" — the next word</td><td>a full stop .28, then return .27 · go .11</td><td><strong>think .62</strong> · remember .25 · stop .04</td></tr><tr><td>"The north <code>&lt;mask&gt;</code> is gone."</td><td>coast .25 · wind .19 · side .15</td><td>america .32 · beauty .26 · telegraph .06</td></tr></tbody></table>
<p>Three things fall out of that. <strong>The instrument works</strong> — the parent produces clean continuations including at the <em>end</em> of a sentence, so a masked LM answering "the next word" is a legitimate query and the distribution-shift objection we expected does not apply at single-step. <strong>The post-training did not destroy the language ability</strong> — a guard lowering his <strong>gun</strong> is a better answer than the parent's "horse". And it is <strong>unevenly</strong> degraded: "the north ___" collapses to <em>america</em> and <em>beauty</em>, so the encoder has been specialised away from free-form completion.</p>
<p>So: yes, mechanically — and only with a language head bolted back on, which costs about <strong>98.7M parameters</strong> for the parent's untied decoder. That is more than double the <strong>42.2M</strong> in all 22 encoder layers, entirely because the vocabulary is 256k. And it is single-step only: generating means re-encoding the whole prefix for every word, with no cache. Which is the conclusion the vendor reached from the other side, in the sentence quoted at the top of this section.</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-conformal-set-that-abstains-on-everything">The Conformal Set That Abstains on Everything<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#the-conformal-set-that-abstains-on-everything" class="hash-link" aria-label="Direct link to The Conformal Set That Abstains on Everything" title="Direct link to The Conformal Set That Abstains on Everything" translate="no">​</a></h3>
<p>One more claim deserved the same test, because it is the one that would matter most. A poster for a 0.8B competitor, <strong>vega</strong>, advertises <em>a conformal set at a chosen risk level α, plus abstain and unbound flags</em> — abstention by construction, with a coverage guarantee, rather than a hope that the model learned to decline. If that worked it would answer this whole post.</p>
<p>We ran it on the same 24 in-scope and 24 off-topic requests, the same 11 tools, at three risk levels — a conformal set shrinks as α rises, so one α is not a result:</p>
<table><thead><tr><th>α</th><th>off-topic abstain</th><th>in-scope abstain</th><th>in-scope correct</th><th>conformal set</th><th>mean latency</th></tr></thead><tbody><tr><td>0.05</td><td><strong>22 of 24</strong></td><td><strong>22 of 24</strong></td><td>15 of 24</td><td>1–3 elements</td><td>551 ms</td></tr><tr><td>0.10</td><td><strong>22 of 24</strong></td><td><strong>22 of 24</strong></td><td>15 of 24</td><td>1–2 elements</td><td>552 ms</td></tr><tr><td>0.20</td><td><strong>22 of 24</strong></td><td><strong>22 of 24</strong></td><td>15 of 24</td><td><strong>empty for all 48</strong></td><td>551 ms</td></tr></tbody></table>
<p><strong><code>abstain</code> fires on 22 of 24 requests on both sides.</strong> Every row below 0.4 confidence abstained — <strong>38 of 38</strong> — and 6 of the 10 above it did too. Mean confidence separates off-topic from in-scope by <strong>0.030</strong> (0.298 against 0.328). The flag is a confidence gate, not a scope judgement, and it is the same wall this post documented for Needle — now in the model whose headline feature is principled abstention.</p>
<p><strong>The conformal set is a cliff, not a curve.</strong> At α ≤ 0.1 it returns a singleton or a pair: never empty, and never <em>wide</em> either, so it cannot say "I can't tell". At α = 0.20 it returns <code>None</code> for all 48. There is no α at which the set does the job.</p>
<p>The likely reason is in the card: <em>"each adapter route carries its own calibration vector."</em> This checkpoint is built for a public-intents label space, and an 11-tool router is outside that route, so its calibration and conformal tables do not apply. The package ships <code>fit()</code> — five-fold, and it <strong>refuses</strong> a head at or below chance — which is the documented way to adapt it. So the honest conclusion is narrower than "it does not work":</p>
<blockquote>
<p><strong>Conformal abstention is not portable to a new vocabulary without refitting.</strong></p>
</blockquote>
<p>That is this post's own lesson a third time: a route that exists is not a route that works. It matters because "conformal set + abstain: yes" reads as a capability you get, not one you have to fit.</p>
<p>On speed we will not score a point we did not earn: the run took <strong>551 ms</strong> on an Apple M1 with 16 GB, but both fast kernels were missing and the runtime says so — <em>"correct but much slower"</em> — so that is an upper bound, not a like-for-like test of the poster's 75 ms. The contradiction worth recording needs no hardware of mine: the model's own card says <strong>280 ms on Apple M-series</strong>.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-it-cannot-do">What It Cannot Do<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#what-it-cannot-do" class="hash-link" aria-label="Direct link to What It Cannot Do" title="Direct link to What It Cannot Do" translate="no">​</a></h2>
<ul>
<li class=""><strong>It is 24 hand-written requests per side, not a benchmark.</strong> The differences are large enough to act on; they are not precision measurements, and the sweep above shows how sharp the sensitivity to framing is.</li>
<li class=""><strong>One tool surface, one language, one working domain.</strong> These models are classifiers over text. Every number here is out-of-domain for the encoders involved.</li>
<li class=""><strong>Nothing was fine-tuned.</strong> All three engines ran zero-shot. The largest advertised gains in this field come from per-product fine-tuning, which we did not do.</li>
<li class=""><strong>No engine was tested inside a live agent loop.</strong> This measures whether the decision is right, not whether a turn improves.</li>
<li class=""><strong>CLM was read, not run.</strong> Reaching it means vLLM plus 16 GB of Qwen3-8B, which is not worth an afternoon to confirm something its own schema already settles.</li>
<li class=""><strong>Julia-1's own abstentions do not come from the model.</strong> Its metrics report coverage and an <code>abstained</code> status on Banking77, but the word does not appear in any code file it ships, and its reproduction script always returns a choice. Whatever produced those abstentions lives in a Banking77-specific harness that is not in the repository.</li>
<li class=""><strong>Julia-1's probabilities are declared uncalibrated and are then cosmetically sharpened.</strong> Its policy file sets <code>"calibration": null</code>, and a presentation step rounds a winner above 0.95 with all others under 0.045 to a one-hot <code>1.0</code>. That is a display rule, disclosed in the docstring, and it is why confident answers read as exactly <strong>1.000</strong> in our table. Do not read those ones as measured certainty.</li>
<li class=""><strong>Decision 3.0's own numbers are its own.</strong> Its card marks d3's results <em>internal evaluation</em> while the competitors' come from <em>live board data</em>, and the card does not say who operates the board. Treat the ordering as informative and the absolute values as unverified.</li>
<li class=""><strong>The index's floor is unstated.</strong> <strong>28.82</strong> at 0.59B against <strong>64.7</strong> at 26.09B is a large gap, but with no published floor neither number means anything on its own, and we are not going to guess one.</li>
<li class=""><strong>Decision 3.0 loads with <code>trust_remote_code=True</code>.</strong> That runs code shipped in the repository, so it is a supply-chain decision rather than a model download, and it is worth treating as one.</li>
<li class=""><strong>A conformal set is only as portable as its calibration.</strong> vega's tables are fitted per adapter route, so on a vocabulary it was not fitted for the set is a singleton or empty rather than a measure of doubt — measured above, at three risk levels.</li>
<li class=""><strong>A refusal is not a safety property.</strong> "Nothing applies" is a scope judgement. It is not a check that a request is <em>safe</em>, and nothing here should be put in a security path in place of a deterministic one.</li>
</ul>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-we-learned">What We Learned<a href="https://bayrak.dev/blog/openjev-nine-reimplementations-refusal-test#what-we-learned" class="hash-link" aria-label="Direct link to What We Learned" title="Direct link to What We Learned" translate="no">​</a></h2>
<p><strong>What worked:</strong> three things, all cheap. The first is the control set — 24 off-topic requests that no tool can serve — which is the only reason "3 of 24" is a number at all rather than a story about a model that seemed fine. The second is running the vendor's own example <em>before</em> judging it on ours; that single control separated "broken model" from "mis-shaped test" in one run, and the first number we would otherwise have published was wrong. And a third, quieter one: sometimes the answer is in the source rather than the weights. CLM's inability to refuse is not a measurement, it is a grep, and it took a minute. Julia-1 made the same point from the other direction — its published SHA-256 let us verify 577 MB of weights before running anything, and its own policy file told us not to trust its calibration before we had formed an opinion about it.</p>
<p><strong>What didn't — nine of our own instruments, all caught before publication.</strong> A C API whose return value is not a byte length, which silently truncated every response into what looked like a model that refuses everything. An off-by-one in our own validator that marked every call as ungrounded and turned a real signal into noise. A 12-label test that was mis-shaped for the model's label template. A summary line that reported "4 refusals" in a run where zero occurred, and whose first fix reported 3 — both wrong, in a log that a reader would have believed instead of the rows. A sample of 12 that let us write "it never refuses" when 24 makes it refuse three times. A sweep we declared unreproducible and marked <em>do-not-cite</em> because we had read its log file while the job was still running; the file was complete, and we had condemned valid evidence on a stale read. A version pin we ignored: Julia-1 declares <code>transformers&gt;=5.0,&lt;5.1</code>, our shared environment had 5.17, and their encoder patch broke in a way that looked like a model failure until we built the environment they actually specified. A language-head probe that returned pure subword soup on every prompt — which was briefly read as "the post-training destroyed the vocabulary geometry", until a control on the parent model showed that we had skipped the prediction head ModernBERT applies <em>before</em> the decoder, and the soup was our own. And a parser that assumed a field was never null: the conformal probe crashed at α = 0.20 because <code>prediction_set</code> comes back as <code>None</code> there, and took two completed alphas down with it — fixed by normalising the answer and writing results after each one. Three of those generalise. <strong>A background job's output is not evidence until the job has exited.</strong> When a release pins a dependency range, that range is part of the artifact. And an optional field is optional: normalise the answer before you parse it, and never let a late failure discard work you have already done.</p>
<p><strong>The uncomfortable part:</strong> we published an explanation and the next release falsified it. Our claim was that abstention is a property of the schema — put a "none of these" option in the contract and the model has somewhere to put the answer. The first half is true and the second half is not: Julia-1 was offered exactly that option and used it zero times in 24, then lost accuracy for having been given it. What we got wrong was treating representability as sufficient, when it is only necessary — Verdict's route works because it was trained with an objective that rewards honest abstention, not because the label existed. That is a narrower claim than the one we first made, and it is the one the evidence supports. It is also the reason to run a control you did not design the model for: the 24 requests could only ever have shown us <em>that</em> the engines differ, and it took adding one option to a third engine to show us <em>why</em>.</p>
<hr>
<p>Every number above is re-read from committed run artifacts by <a href="https://github.com/brsbyrk/bayrak.dev/blob/main/scripts/check-openjev-post.py" target="_blank" rel="noopener noreferrer" class="">scripts/check-openjev-post.py</a> — the Needle counts are parsed out of the harness logs, the Verdict and Julia-1 counts out of the per-request JSON, and the CLM arithmetic is re-derived from its config. It fails if any figure drifts, or if a figure appears in this post with no derivation behind it. <a href="https://github.com/brsbyrk/bayrak.dev/blob/main/scripts/mutation-test-openjev-checker.py" target="_blank" rel="noopener noreferrer" class="">scripts/mutation-test-openjev-checker.py</a> corrupts the post ten ways and requires every corruption to be caught, so that the checker is not merely a thing that passes. <code>--live</code> re-fetches the pinned vendor claims, which are the parts that can change under a published post.</p>
<p><strong>Updating this post.</strong> This is meant to be re-run, not rewritten: the harness, the evidence rows for all three engines, and the checker are all committed. To add a model, run it on the same 24 + 24 requests against the same 11 tools, add its rows, add one line to the family table, and let the checker decide whether the prose still holds.</p>
<p><strong>Updated 2026-09-26:</strong> Julia-1 added and measured, which required correcting the explanation this post first gave for why abstention separates the engines. The correction is in place above rather than in a footnote, because we would rather the post be wrong in public for a day than quietly right later.</p>
<p><strong>Updated 2026-10-11:</strong> the field acquired a leaderboard. Decision 3.0 and the Jev Decision Index are added above, with the one line on that board we would not accept without a control set — plus our own measurement of whether a Jev-style model can emit a word at all, which is the question the whole category is defined by refusing to answer. Nothing in the measured core changed.</p>
<p><strong>Updated 2026-10-18:</strong> we tested the strongest version of the abstention idea — a conformal set at a chosen risk level, which would abstain by construction rather than by training. On our 24 + 24 surface it abstained on 22 of 24 requests on <em>both</em> sides, and its conformal set was a singleton or nothing at all. The mechanism is real; the calibration behind it does not travel to a new vocabulary without refitting. That is the same lesson as the rest of this post, so it belongs here rather than in a separate note.</p>]]></content:encoded>
            <category>research</category>
            <category>models</category>
            <category>inference</category>
            <category>agents</category>
        </item>
        <item>
            <title><![CDATA[claimaudit — Three Physics Ceilings, One Control Set, and No Buyer]]></title>
            <link>https://bayrak.dev/blog/claimaudit-three-ceilings-no-buyer</link>
            <guid>https://bayrak.dev/blog/claimaudit-three-ceilings-no-buyer</guid>
            <pubDate>Tue, 22 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[We built a tool that audits public technical claims against the derived hard ceiling of their own physics — three domains, 49 tests, standard library only — and it caught real errors in real claims. Then we wrote down the criteria for whether anyone would pay for it, before looking, and the answer came back no. Not because the tool fails, but because every route to the money sits behind an accreditation wall.]]></description>
            <content:encoded><![CDATA[<p>A claim can be exciting, well-funded, and arithmetically impossible at the same time, and the arithmetic is usually the part nobody checks. This project started as an attempt to answer a different question — whether a cheap abundant material could be turned into something valuable from a laptop — and it ended somewhere more useful: with a small tool that audits public technical claims against the <strong>derived hard ceiling of their own domain</strong>, three working domains, and a rigorously negative answer to the question of whether anyone would pay for it.</p>
<!-- -->
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-question-and-the-wrong-answer">The Question, and the Wrong Answer<a href="https://bayrak.dev/blog/claimaudit-three-ceilings-no-buyer#the-question-and-the-wrong-answer" class="hash-link" aria-label="Direct link to The Question, and the Wrong Answer" title="Direct link to The Question, and the Wrong Answer" translate="no">​</a></h2>
<p>The prompt was a video arguing that rare earths aren't actually rare. That is true, and it led somewhere unexpected. The interesting question underneath was whether a solo person with a PC and no lab could take an abundant, undervalued material and transform it into something valuable.</p>
<p><strong>No.</strong> Materials work is gated three ways: knowledge (a simulation can sometimes close this), a wet lab (it cannot be closed from a keyboard), and capital (a plant is not a weekend project). Almost every candidate lands behind at least two of the three. Rare-earth-free magnets are the clean example — the physics is fine, every contender has adequate intrinsic magnetism, and the blocker is synthesis and phase stability, which is bench work.</p>
<p>But three independent research passes looking into it all landed on the same bottleneck, and it wasn't materials. It was that <strong>nobody verifies the numbers.</strong> Claims circulate through press releases, trade coverage, and citation chains, and almost nobody does the two-line arithmetic that would show a claim cannot be true.</p>
<p>That is a PC-first, zero-capital activity with a real intellectual core, so it became the project.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-rule-that-made-it-work">The Rule That Made It Work<a href="https://bayrak.dev/blog/claimaudit-three-ceilings-no-buyer#the-rule-that-made-it-work" class="hash-link" aria-label="Direct link to The Rule That Made It Work" title="Direct link to The Rule That Made It Work" translate="no">​</a></h2>
<p>The tool audits a claim by computing the <strong>hard ceiling its own physics imposes</strong>, from published constants, rather than by comparing it to competitors. Three design rules turned out to matter more than the physics:</p>
<ul>
<li class=""><strong>Derive the ceiling, don't cite it.</strong> Shockley-Queisser isn't quoted from a textbook; it's computed by detailed balance over an embedded copy of the real ASTM G173 AM1.5G spectrum. Integrating that embedded table returns <strong>998.8 W/m²</strong> against the <strong>1000.4 W/m²</strong> the standard documents — 0.16% apart, which is the check that the spectrum and the integrator are both right <em>before</em> any efficiency is computed.</li>
<li class=""><strong>Keep a control set of real shipping devices the audit must never flag.</strong> This is the rule that did all the real work, and it is the one we would have skipped.</li>
<li class=""><strong>Keep the naive bound as a regression test.</strong> Every domain starts with the wrong version of its own ceiling. Keeping it around, and asserting that real hardware <em>breaks</em> it, documents exactly why the rigorous version exists.</li>
</ul>
<p>Plus a fourth: <strong>verdicts that refuse.</strong> When a bound doesn't apply, the tool says so instead of quietly widening until the claim passes.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="three-domains-three-different-failures">Three Domains, Three Different Failures<a href="https://bayrak.dev/blog/claimaudit-three-ceilings-no-buyer#three-domains-three-different-failures" class="hash-link" aria-label="Direct link to Three Domains, Three Different Failures" title="Direct link to Three Domains, Three Different Failures" translate="no">​</a></h2>
<p>The three domains were chosen so that each tests something the previous one couldn't.</p>
<table><thead><tr><th>Domain</th><th>Ceiling</th><th>What it caught</th></tr></thead><tbody><tr><td><strong>Magnets</strong></td><td>(BH)max ≤ min(Br²/4μ₀, Br·Hcj)</td><td>A vendor headline that is impossible at the vendor's own disclosed numbers</td></tr><tr><td><strong>Solar</strong></td><td>Shockley-Queisser, derived over AM1.5G</td><td>A <strong>category error</strong> reported as a physics breakthrough</td></tr><tr><td><strong>Heat pumps</strong></td><td>COP ≤ T_H/(T_H − T_C), exact from the second law</td><td>A <em>legitimate</em> claim that a naive reviewer rejects</td></tr></tbody></table>
<p><strong>Magnets.</strong> The remanence ceiling <code>Br²/4μ₀</code> gives 56.3 MGOe at Br = 1.5 T, but that ceiling only applies if the coercivity is high enough. With coercivity included, the binding limit is <code>Br·Hcj</code>, and at the 2.1 kOe coercivity actually reported for a 36 MGOe claim, the ceiling is <strong>31.5 MGOe</strong>. The claim exceeds it by 12% and is flagged <code>IMPOSSIBLE</code>. At the company's own higher target coercivity the ceiling rises to 49.5 MGOe, the claim fits — and a second check then fails it on a different ground: reaching it requires a <code>squareness</code> of 2.91× the straight-line relation, against a best-ever-measured <strong>2.79×</strong>. The best peer-reviewed measured result in that family, 20 MGOe at 1.91 kOe, passes cleanly at a 28.7 MGOe ceiling. Three claims in one family, three different verdicts, all from arithmetic.</p>
<p><strong>Solar.</strong> A widely reported story said solar cells had hit <strong>130% quantum yield</strong>, "smashing a 65-year limit." As a power-conversion efficiency that would be impossible — the single-junction ceiling peaks at <strong>33.7% near Eg = 1.34 eV</strong>, and 130% exceeds it roughly fourfold. But quantum yield is not efficiency. It counts excitations per absorbed photon, and it legitimately exceeds 100%: singlet fission turns one singlet into two triplets, so the real cap is <strong>200%</strong>, and 130% sits comfortably under it. The same number gets opposite verdicts depending on which quantity is meant, which is why the tool has a <code>NOT_APPLICABLE_FOR_SQ</code> verdict and uses it.</p>
<p><strong>Heat pumps.</strong> This is the domain that inverted the tool's posture. The dominant public error here is not an overclaim but a naive <strong>debunking</strong>: a heat pump rated COP 4.8 gets reported as "480% efficient" and dismissed, because nothing can exceed 100%. But COP counts heat <em>moved</em> per unit of work, and the heat comes from outside the building. <strong>The naive rule rejects all 8 of the real, manufacturer-datasheet heat pumps in the control set</strong> — and the second law accepts all 8. The best real machine reaches <strong>57%</strong> of its Carnot allowance; the tool flags claims above the envelope, not above 100%.</p>
<p>The domain-specific trap is subtler than the naive rule. The ceiling is only as tight as the temperatures you feed it. Use indoor room air as the hot reservoir and a claim of COP 12 sails through; use the delivery-water temperature the machine actually produces and the same claim is <code>IMPOSSIBLE</code>. The tool therefore refuses to audit a COP claim that doesn't state its temperatures. In the wild, that is the most common form — "COP up to 5.1" — and it is unverifiable as written.</p>
<p>The same corpus also contains this domain's version of the magnet failure: manufacturers' seasonal ratings reach <strong>5.65</strong>, while the government's own monitored field trial median is <strong>2.80</strong> across <strong>742 homes</strong>. Both numbers are honest. They are not the same quantity, and presenting the first as the second is the same move as presenting a development target as a measured result.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-control-set-is-the-product">The Control Set Is the Product<a href="https://bayrak.dev/blog/claimaudit-three-ceilings-no-buyer#the-control-set-is-the-product" class="hash-link" aria-label="Direct link to The Control Set Is the Product" title="Direct link to The Control Set Is the Product" translate="no">​</a></h2>
<p>The finding worth keeping is not any of the physics. It is that <strong>every domain's first bound attempt false-positived on real shipping hardware.</strong></p>
<p>The naive magnet bound, <code>Br·Hcj/4</code>, condemns shipping sintered ferrite. A single-junction-only solar check condemns certified tandem cells that legitimately run above the 1-junction curve. The naive heat-pump rule condemns every heat pump ever built. In each case the <em>physics</em> was fine and the <em>bound</em> was the thing that was wrong, in the direction that makes a tool look impressive and be useless.</p>
<p>Only the control set caught it, every time. And that is why the same failure mechanism never repeats across domains while the <em>shape</em> of the failure always does — so this generalises as a per-domain discipline with a control set, not as one clever general tool.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="then-we-asked-who-would-pay">Then We Asked Who Would Pay<a href="https://bayrak.dev/blog/claimaudit-three-ceilings-no-buyer#then-we-asked-who-would-pay" class="hash-link" aria-label="Direct link to Then We Asked Who Would Pay" title="Direct link to Then We Asked Who Would Pay" translate="no">​</a></h2>
<p>At this point there was a working tool, 49 passing tests, and four of those tests defending real devices against false positives. The obvious next move was a fourth domain. The useful move was to stop and ask whether anyone buys this.</p>
<p>So we wrote the kill criteria down first — committed, dated, before any research — because a criterion written after seeing the data is a rationalisation. The question was narrow: <strong>is there a buyer who will pay real money for auditing a technical claim against the hard ceiling of its domain?</strong> Split into four independently falsifiable parts: pain, payment, reachability, scalability.</p>
<p>The verdict is <strong>kill.</strong> Three of three stopping conditions met; two of three continuation conditions, failing the one that matters.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-the-money-actually-looks-like">What the Money Actually Looks Like<a href="https://bayrak.dev/blog/claimaudit-three-ceilings-no-buyer#what-the-money-actually-looks-like" class="hash-link" aria-label="Direct link to What the Money Actually Looks Like" title="Direct link to What the Money Actually Looks Like" translate="no">​</a></h2>
<p>Verification <em>is</em> paid for, generously, and the price ladder is the clearest thing we found:</p>
<table><thead><tr><th>What is bought</th><th>Price</th></tr></thead><tbody><tr><td>Mechanical image-integrity scan (ImageTwin)</td><td><strong>€29</strong> per scan</td></tr><tr><td>Expert-network project (Inex One)</td><td><strong>$500</strong></td></tr><tr><td>Engineering due-diligence consultancy (Exponent, from its own 10-K)</td><td><strong>$225–$1,375/hour</strong></td></tr><tr><td>Expert witness (SEAK 2024 medians)</td><td><strong>$450–$475/hour</strong></td></tr><tr><td>Independent lab test</td><td><strong>$5k–$50k+</strong></td></tr><tr><td>Analyst subscription, often sole-source</td><td><strong>$50k–$970k/year</strong></td></tr></tbody></table>
<p><strong>Every one of those is a different job.</strong> Not one of them is "tell me whether this claim is physically achievable." The best-funded adjacent service is Exponent Inc., a Nasdaq-listed company whose explicit business is technical due diligence, billing a rate ceiling that rose about 40% in three years. That is unambiguous evidence the <em>skill</em> is valuable — and it is sold through firms and credentials, contracted enterprise-to-enterprise, never self-serve.</p>
<p>Meanwhile the open-ended version of the job is done <strong>for free at scale</strong>: we catalogued <strong>28 free artifacts</strong> doing this work (expert blog posts, trade-press critiques, forum arithmetic, YouTube debunks, publicly funded null replications) against <strong>13 paid services in adjacent slices</strong> and <strong>zero</strong> paid products selling the open-ended verdict to a general buyer. The public-interest version is philanthropy-funded: Retraction Watch ran the leading claim-verification database as, in its own words, "a volunteer activity" for 12 of its 13 years, then abandoned its licensing revenue model entirely in September 2023 and handed the database over to be made free.</p>
<p>The position is <strong>unoccupied for individuals and fully occupied for institutions</strong> — and the institutions are behind accreditation, professional-indemnity insurance, tender turnover thresholds, court qualification, or expert-network seniority screening. Identifiable buyer, unreachable buyer.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="why-it-is-dead">Why It Is Dead<a href="https://bayrak.dev/blog/claimaudit-three-ceilings-no-buyer#why-it-is-dead" class="hash-link" aria-label="Direct link to Why It Is Dead" title="Direct link to Why It Is Dead" translate="no">​</a></h2>
<p>Three reasons, and the first one is the one that would be hardest to fix.</p>
<p><strong>The tool is pointed at the wrong failure mode.</strong> A ceiling audit diagnoses <em>sincere over-belief</em> — someone who genuinely thinks their impossible thing works. The documented losses are overwhelmingly <em>intentional misstatement</em>: the SEC charged fraud, requiring scienter, in case after case, and in one instance a company claimed <strong>87 vehicles sold when the number was zero</strong>. Fraud is caught by document forensics, whistleblowers and short sellers. None of those is a physics service.</p>
<p><strong>The sophisticated buyers already verify in-house and walk away.</strong> When a major automaker diligenced a hydrogen-truck startup with an "army of people," it restructured the deal and dropped the equity stake rather than buying an audit. When another ran battery cells through its own lab before investing, that was in-house capability too. The people with money to lose and the discipline to check already check.</p>
<p><strong>The free supply is fast and correct.</strong> A contested superconductor claim went from announcement to expert consensus in about two weeks, for free. An impossible propulsion claim died to a single null replication from a publicly funded lab. Envia's battery claims died to a customer's own test bench plus trade press. Competing with €0 supply that is fast and usually right is not a business.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-it-cannot-do">What It Cannot Do<a href="https://bayrak.dev/blog/claimaudit-three-ceilings-no-buyer#what-it-cannot-do" class="hash-link" aria-label="Direct link to What It Cannot Do" title="Direct link to What It Cannot Do" translate="no">​</a></h2>
<ul>
<li class=""><strong>It cannot tell you a claim is true.</strong> <code>CONSISTENT</code> means internally consistent with the physics, not verified. A claim can pass every bound and still be a lie.</li>
<li class=""><strong>It cannot audit an under-specified claim.</strong> No temperatures, no conditions, no numbers — no verdict. That is the majority of marketing claims.</li>
<li class=""><strong>It cannot see the thing that actually costs money.</strong> Intentional misstatement is invisible to a physics bound.</li>
<li class=""><strong>It cannot scale across domains for free.</strong> Each new domain needed its own derivation and its own control set. The reusable part is the method, not the code.</li>
<li class=""><strong>It did not find a buyer.</strong> The two load-bearing negatives — nobody asks, and no well-funded product died in this space — are absence of evidence, not proof.</li>
</ul>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-we-learned">What We Learned<a href="https://bayrak.dev/blog/claimaudit-three-ceilings-no-buyer#what-we-learned" class="hash-link" aria-label="Direct link to What We Learned" title="Direct link to What We Learned" translate="no">​</a></h2>
<p><strong>What worked:</strong> deriving the ceiling rather than citing it, and controlling against real hardware. Both were cheap, and together they are the whole method. The specific rule that paid off most is keeping a <strong>set of devices the tool must never flag</strong> — without it, three separate domains would have shipped a confidently wrong bound that condemned real, shipping products. A tool that only ever says "no" is a debunking machine; the control set is what makes it an auditing capability.</p>
<p><strong>What didn't:</strong> the assumption that a working, rigorous, error-finding tool is adjacent to a business. It isn't adjacent to anything. Building three domains taught us nothing about willingness to pay, and the honest sequencing is the reverse of what felt natural — write the criteria down, then check, before the sunk cost makes the question feel like an attack on the work. Once the tool existed and worked, the temptation to reinterpret a negative result as a reason to build a fourth domain was very strong, and the only thing that blocked it was the criterion already being committed to git with a date on it.</p>
<p><strong>The uncomfortable part:</strong> the market we found is real and well paid, and it is a labour market rather than a founder market. Engineering consultancies bill hundreds of dollars an hour for exactly this judgement; expert networks pay experts a few hundred an hour and charge clients several times that. The work is worth a great deal <em>per hour</em> and nothing as a solo product, because the buyer contracts with a firm and a credential, not with a person who has neither. That is a different problem from "the idea was bad," and it is not solvable by executing the same idea better.</p>
<p>The tool survives as a capability, and it has already earned its keep twice — once by showing a magnet headline was arithmetically impossible at its own reported numbers, and once by showing that a solar "breakthrough" was a definition being swapped rather than a limit being broken. Both were public claims that had travelled a long way on citation. Neither needed a lab, a budget, or anyone's permission.</p>
<hr>
<p>The numbers in this post are read out of committed run artifacts and re-derived by <a href="https://github.com/brsbyrk/bayrak.dev/blob/main/scripts/check-claimaudit-post.py" target="_blank" rel="noopener noreferrer" class="">scripts/check-claimaudit-post.py</a>, which re-runs the audit tool's own ceilings, re-integrates the AM1.5G spectrum, and checks every market figure against the evidence document it came from. It fails if any of them drifts.</p>
<p>The audit tool and its research streams are not published. The evidence behind this post lives with the tool in <code>docs/VERDICT.md</code> and <code>docs/evidence/</code> — four research streams with every claim labelled fact, inference or not-found.</p>]]></content:encoded>
            <category>physics</category>
            <category>verification</category>
            <category>research</category>
            <category>business</category>
        </item>
        <item>
            <title><![CDATA[Taalas — a Model Etched Into Silicon, and the Ceiling It Actually Breaks]]></title>
            <link>https://bayrak.dev/blog/taalas-etched-model-silicon</link>
            <guid>https://bayrak.dev/blog/taalas-etched-model-silicon</guid>
            <pubDate>Thu, 17 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[AMD has agreed to acquire Taalas, whose HC1 chip hard-wires Llama 3.1 8B into mask ROM and serves 17,000 tokens/sec to a single user. We re-derive the physics from published constants: the 299 tok/s bandwidth ceiling it breaks is real, the ~74× latency win is real, and the attached '1000× efficiency' is a bracket that spans 400× to 1,600× depending on which precision you compare against — because it measures GPU utilisation at batch size 1, not a result in physics.]]></description>
            <content:encoded><![CDATA[<p>A Toronto startup with 24 employees has built a chip whose 53 billion transistors <strong>are</strong> the weights of a language model rather than a place to put them. On Llama 3.1 8B it serves <strong>17,000 tokens per second to a single user</strong> — 48× the 353 tok/s Taalas measured on a Blackwell B200, and 8.6× the 1,981 tok/s that Cerebras, the previous leader, manages on the same model. AMD announced on 6 August 2026 that it has agreed to acquire the company. Taalas's homepage claims Hardcore Models are "1000x more efficient than their software counterparts," which is the kind of number that usually means nothing. Here it means something specific and much narrower than it sounds — and the physics underneath is not only real, it names a ceiling that no amount of GPU engineering gets past.</p>
<!-- -->
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="first-what-is-actually-established">First, What Is Actually Established<a href="https://bayrak.dev/blog/taalas-etched-model-silicon#first-what-is-actually-established" class="hash-link" aria-label="Direct link to First, What Is Actually Established" title="Direct link to First, What Is Actually Established" translate="no">​</a></h2>
<p>Everything below distinguishes three tiers of evidence, because the interesting part of this story is how they differ.</p>
<p><strong>Primary, from Taalas</strong> (<a href="https://taalas.com/products/" target="_blank" rel="noopener noreferrer" class="">product page</a>, <a href="https://taalas.com/the-path-to-ubiquitous-ai/" target="_blank" rel="noopener noreferrer" class="">Bajic's launch post</a>): 815 mm², TSMC 6nm, 53 billion transistors, 17k tokens/sec/user on Llama 3.1 8B, 1k-in/1k-out. Also on the record: the quantization is a custom <strong>3-bit base type with 3-bit and 6-bit parameters mixed</strong>, which Bajic states "introduces some quality degradations relative to GPU benchmarks."</p>
<p><strong>Primary, from AMD</strong> (<a href="https://newsroom.amd.com/news/amd-acquires-taalas-ai-inference/" target="_blank" rel="noopener noreferrer" class="">newsroom</a>): a <em>definitive agreement</em>, "subject to customary closing conditions and regulatory approvals." The release does not state a price, and the deal is not closed — so the shorts are slightly ahead of reality.</p>
<p><strong>Where the comparison numbers live, and a correction.</strong> The competitor figures come from <a href="https://taalas.com/h-content/uploads/2026/02/graph.png" target="_blank" rel="noopener noreferrer" class="">a chart image</a> on Taalas's own site, which means they cannot be quoted from page text and have to be read. We read it: <strong>Taalas HC1 16,960 · Cerebras 1,981 · SambaNova 932 · Groq 594 · Nvidia B200 353 · Nvidia H200 230</strong> tokens/sec/user. The B200 figure is Taalas-measured; the Groq, SambaNova and Cerebras figures are sourced on that chart to Artificial Analysis. An earlier draft of this post used 350 and 200 for the two Nvidia baselines, taken from secondary coverage rather than the chart. Those were low: the correct baselines are 353 and 230, which makes the head-to-head ratios 48× and 74×, not 48× and 85×. Taalas's own written claim is the more modest "<a href="https://taalas.com/the-path-to-ubiquitous-ai/" target="_blank" rel="noopener noreferrer" class="">nearly 10X faster than the current state of the art</a>" — which checks out against Cerebras at 8.6×.</p>
<p><strong>Third-party</strong>: <a href="https://www.nextplatform.com/compute/2026/02/19/taalas-etches-ai-models-onto-transistors-to-rocket-boost-inference/4092140" target="_blank" rel="noopener noreferrer" class="">The Next Platform</a> got the only substantive technical interview. The founder history is worth knowing: Ljubisa Bajic founded Tenstorrent, a general-purpose AI accelerator company, then left to build Taalas around the opposite bet; all three co-founders came from Tenstorrent, and before that AMD.</p>
<p><strong>Not established</strong>: there is no independent third-party benchmark. The B200 baseline was run by Taalas.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-ceiling-that-explains-everything">The Ceiling That Explains Everything<a href="https://bayrak.dev/blog/taalas-etched-model-silicon#the-ceiling-that-explains-everything" class="hash-link" aria-label="Direct link to The Ceiling That Explains Everything" title="Direct link to The Ceiling That Explains Everything" translate="no">​</a></h2>
<p>LLM decoding is not compute-bound. To emit one token you must touch every weight exactly once, and at 16-bit precision that is 16.06 GB of data that has to be hauled out of DRAM <strong>per token, forever</strong>. This puts a hard ceiling on any conventional accelerator, from any vendor:</p>
<table><thead><tr><th>Chip</th><th>HBM bandwidth</th><th>Weights read per token</th><th>Ceiling</th><th>Taalas's measured figure</th></tr></thead><tbody><tr><td>H200</td><td>4.8 TB/s</td><td>16.06 GB</td><td><strong>299 tok/s</strong></td><td>230 (77% of ceiling)</td></tr><tr><td>B200</td><td>8.0 TB/s</td><td>16.06 GB</td><td><strong>498 tok/s</strong></td><td>353 (71% of ceiling)</td></tr></tbody></table>
<p>That table is the whole story, and it cuts both ways. Read one way, it says modern GPUs are nowhere near the theoretical limit. Read the other, and it says they are already at <strong>71–77% of a wall that physics put there</strong> — which is a well-engineered chip doing its job, not a chip that can be optimised into 17,000 tok/s. You cannot get to 17,000 by making the memory faster. You have to stop reading the weights.</p>
<p>The self-consistency here is worth noting, and it survives the correction above. Taalas's published Nvidia baselines sit at 71% and 77% of the ceilings computed from published bandwidth — that is, they are <em>generous</em> to Nvidia. A vendor inflating its own ratio publishes low competitor numbers; Taalas published high ones. Their 48× is therefore a more conservative claim than it needed to be.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="a-weight-that-is-not-data">A Weight That Is Not Data<a href="https://bayrak.dev/blog/taalas-etched-model-silicon#a-weight-that-is-not-data" class="hash-link" aria-label="Direct link to A Weight That Is Not Data" title="Direct link to A Weight That Is Not Data" translate="no">​</a></h2>
<p>The mechanism is a <strong>mask ROM</strong>. In a mask ROM a bit is not a charge in a cell — it is the <em>presence or absence of a contact</em> at a wire crossing. Value is geometry in a metal layer. Bajic's description of the consequence:</p>
<blockquote>
<p>"We have got this scheme for the mask ROM recall fabric — the hard-wired part — where we can store four bits away and do the multiply related to it — everything — with a single transistor. So the density is basically insane. And this is not nuclear physics — it is fully digital. It is just a clever trick that we don't want to broadcast."</p>
</blockquote>
<p>The lever is that a weight is a <strong>constant</strong>, fixed at fab time. Multiplying by a constant is not really arithmetic — it is a lookup. So there is no multiplier array to build (Bajic calls multipliers "the big boy piece of the computer") and no weight movement at all. Only activations move, and only across the die. The architecture pairs that immutable ROM fabric with a mutable SRAM fabric holding the KV cache and LoRA adapters — the model's skeleton is silicon, its mutable state is not.</p>
<p><strong>Attributed where it belongs, and still unconfirmed.</strong> Taalas is explicit that it is not disclosing the fabric ("we don't want to broadcast"). The circuit-level story that circulates in secondary coverage — that the chip pre-computes all sixteen products of a 4-bit slice at manufacturing time and lets pass transistors <em>select</em> the correct pre-wired channel — is exactly that: secondary coverage, not a Taalas statement. It is a plausible reading of "one transistor stores four bits and performs the associated multiply," and it is not ours to assert. The functional claim is Bajic's, on the record. The mechanism behind it is not.</p>
<p>Two physical consequences follow, and they are why 200 W is credible. Multiplication by a constant bit decomposes into a conditional wire, so the dominant energy term disappears; and a ROM cell that is not being read does not toggle, so dynamic power tracks only the activations moving through. The resulting power density is <strong>0.25 W/mm²</strong> (200 W over 815 mm²) — low enough that standard air cooling suffices, and low precisely <em>because</em> the data is not moving.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="does-the-transistor-budget-add-up">Does the Transistor Budget Add Up?<a href="https://bayrak.dev/blog/taalas-etched-model-silicon#does-the-transistor-budget-add-up" class="hash-link" aria-label="Direct link to Does the Transistor Budget Add Up?" title="Direct link to Does the Transistor Budget Add Up?" translate="no">​</a></h2>
<p>This is the one part of the design that is externally checkable, so it is worth checking. Take Bajic's own ratio — four bits stored and multiplied per transistor — and Taalas's own quantization statement (3-bit base, 3-bit and 6-bit mixed, so call it ~4 bits per parameter on average):</p>
<table><thead><tr><th>Component</th><th>Derivation</th><th>Transistors</th></tr></thead><tbody><tr><td>Weights, etched in ROM</td><td>8.03B params × 4 bits ÷ 4 bits per transistor</td><td><strong>8.0B</strong> (15%)</td></tr><tr><td>KV cache, adders, muxes, clocking, routing</td><td>53B − 8.0B</td><td>~45B (85%)</td></tr></tbody></table>
<p>The arithmetic closes on the stated figure, which is not nothing — a fundamentally different storage scheme that came out three orders of magnitude off the announced transistor count would be a problem. Note what the 15% is and is not: it is the <strong>transistor</strong> share, not the <strong>area</strong> share, and ROM cells and logic have different densities, so the two are not interchangeable. What it does say is that the weights are a minority of the silicon: the rest is the compute fabric and the mutable state, and the KV cache has to live in that budget — which is why Taalas's stated flexibility ("configurable context window size and LoRA fine-tuning") is bounded by how much SRAM was sized at tapeout.</p>
<p>A useful contrast: the B200 carries 208 billion transistors. Its die area is roughly 1,600 mm² across its two dies, which puts it near <strong>130 MTr/mm²</strong> against HC1's 65 MTr/mm² — about double. That area figure is our estimate, not a published NVIDIA number, and the comparison is order-of-magnitude only. Even loosely, though, it makes the point: the ROM chip is <em>less</em> dense, not more. Density was never the problem being solved. Not moving the data was.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="three-numbers-called-efficiency">Three Numbers Called "Efficiency"<a href="https://bayrak.dev/blog/taalas-etched-model-silicon#three-numbers-called-efficiency" class="hash-link" aria-label="Direct link to Three Numbers Called &quot;Efficiency&quot;" title="Direct link to Three Numbers Called &quot;Efficiency&quot;" translate="no">​</a></h2>
<p>This is where the headline claims have to be taken apart, because three quite different figures get flattened into one.</p>
<p><strong>Latency: ~74×, and it is structural.</strong> 17,000 tok/s is 58.8 µs per token, which across 32 transformer layers is <strong>1,838 ns per layer</strong> — for RMSNorm, QKV projection, attention and the MLP combined. For contrast, take the same ceiling table and ask what one <em>layer</em> costs on an H200: 502 MB of weights per layer at 4.8 TB/s is <strong>105 µs of pure memory time</strong>, before a single multiply. That is a 57× gap in memory time alone, and no amount of batching closes it, because it is per-token serial latency either way. End to end against the H200's 230 tok/s the ratio is 74×; against the B200's 353 tok/s, 48×. It is the most defensible number in the whole story.</p>
<p><strong>Energy per token: a few times, not a thousand.</strong> HC1 spends <strong>11.8 mJ per token</strong> at the stated 200 W per card. An H200 serving one stream at 230 tok/s spends 3,043 mJ per token — <strong>259×</strong> more. That is the comparison the headline invites, and it is the wrong one, because batching exists to amortise exactly this. A <em>batched</em> H200 at 20,000 tok/s aggregate is already at 35 mJ per token, only <strong>3×</strong> behind. That number is the load-bearing assumption in this post, so here is the sensitivity rather than a single figure:</p>
<table><thead><tr><th>Batched H200 aggregate</th><th>H200 mJ/token</th><th>Ratio to HC1</th></tr></thead><tbody><tr><td>5,000 tok/s</td><td>140</td><td>12×</td></tr><tr><td>10,000 tok/s</td><td>70</td><td>6×</td></tr><tr><td><strong>20,000 tok/s</strong></td><td><strong>35</strong></td><td><strong>3×</strong></td></tr><tr><td>40,000 tok/s</td><td>17.5</td><td>1.5×</td></tr></tbody></table>
<p>And a second caveat running the other way: 200 W is the per-card figure from the Next Platform interview, while the product page quotes 2.5 kW for a ten-card server — which is 250 W per card including its share of host, NICs and fans. On the higher figure HC1 is 14.7 mJ/token and the honest ratio becomes 2.4×. So the real answer is a range of roughly <strong>2.4× to 3×</strong>, and it is a range because both inputs are.</p>
<p><strong>The 1000× is a utilisation figure, and it has a range too.</strong> Take the B200 at 353 tok/s. That is 353 × 16.06 GFLOP = 5.67 TFLOP/s. Against a dense FP4 peak of 9,000 TFLOP/s that is <strong>0.063% of peak</strong> — invert it and you get roughly <strong>1,600×</strong>. But the denominator is the one place this arithmetic can be pushed, because the B200 never executes in FP4 at single-stream: at FP8 the peak is 4,500 TFLOP/s and the ratio is ~800×, and at FP16, the precision actually being executed, the peak is 2,250 TFLOP/s and the ratio is ~400×. So the honest reading of "1000× more efficient" is: <strong>it is the reciprocal of GPU utilisation at batch size 1, and it brackets between 400× and 1,600× depending on the precision you pick.</strong> That range is a real result — the GPU genuinely runs at a fraction of a percent of peak on a batch-1 decode, because it is starved for bandwidth — and it is not a claim about joules. On joules per FLOP at peak, the B200 is the better chip: 9.00 TFLOP/J against HC1's 1.37 TFLOP/J.</p>
<p>The physical bedrock under all of this is <a href="https://gwern.net/doc/cs/hardware/2014-horowitz-2.pdf" target="_blank" rel="noopener noreferrer" class="">Horowitz's ISSCC 2014 numbers</a>, and they are brutal:</p>
<table><thead><tr><th>Operation</th><th>Energy</th></tr></thead><tbody><tr><td>32-bit floating-point multiply</td><td>3.7 pJ</td></tr><tr><td>On-chip cache access or functional operation (his prose)</td><td>10 pJ</td></tr><tr><td>DRAM I/O</td><td><strong>over 20 pJ per bit</strong></td></tr><tr><td>Full DRAM access</td><td>1–2 nJ</td></tr></tbody></table>
<p>Reading a single weight from DRAM costs many times more than the arithmetic it feeds, and a weight is sixteen bits. Delete the read and most of the bill disappears. That is the entire thesis, and it is a real one.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-it-cannot-do">What It Cannot Do<a href="https://bayrak.dev/blog/taalas-etched-model-silicon#what-it-cannot-do" class="hash-link" aria-label="Direct link to What It Cannot Do" title="Direct link to What It Cannot Do" translate="no">​</a></h2>
<ul>
<li class=""><strong>One model per chip.</strong> A new model is a new tapeout. Only two metal layers change, so it is far cheaper and faster than a full design — Taalas quotes <strong>two months</strong> from an unseen model to deployable cards — but it is a hard discontinuity, not a config file.</li>
<li class=""><strong>Quality is knowingly traded.</strong> Taalas's own words on the 3-bit/6-bit mix: "some quality degradations." Comparing a degraded 8B model at 17k tok/s to a clean one at 230 is not comparing like with like.</li>
<li class=""><strong>Long context is the weak axis.</strong> The KV cache grows per token and lives in fixed on-chip SRAM. Window size is configurable within that budget, but the budget itself is fixed at tapeout.</li>
<li class=""><strong>Mixture-of-experts inverts the economics.</strong> This is our argument rather than a sourced claim. MoE activates a small fraction of weights per token, so a GPU's bandwidth bill scales down with the active fraction. A ROM chip must bake <em>all</em> the experts into silicon to serve any of them, then traverse only some — the full area cost, a fraction of the benefit. It is the architecture that most directly attacks the premise.</li>
<li class=""><strong>One die is one die.</strong> At 815 mm² the part is already at 95% of the 858 mm² reticle limit — the standard single-exposure field for 193i lithography. Anything larger than a mid-sized model has to be pipelined across chips.</li>
<li class=""><strong>No independent verification.</strong> Vendor-run benchmarks, as of the launch.</li>
</ul>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="why-amd-bought-it">Why AMD Bought It<a href="https://bayrak.dev/blog/taalas-etched-model-silicon#why-amd-bought-it" class="hash-link" aria-label="Direct link to Why AMD Bought It" title="Direct link to Why AMD Bought It" translate="no">​</a></h2>
<p>Two things are happening at once. The obvious one: The Next Platform reported that Nvidia had just bought Groq for ~$20 billion, and AMD has no equivalent asset and no CUDA moat to defend. Buying a decode-specialised chip is differentiation by a different route, and AMD's stated plan — integrate it alongside Instinct GPUs in Helios racks — is a division of labour rather than a replacement. GPUs keep prefill, training and flexibility; the hardwired part eats the serial-decode latency problem that GPUs cannot structurally solve.</p>
<p>The less obvious one is who is being bought. Bajic was an AMD architect on hybrid CPU-GPU designs before founding Tenstorrent; his co-founders came through AMD and ATI as well. On the economics, the framing from Taalas is that hardening a model costs about one hundredth of training it. That only works if you need to serve <em>that specific model</em> for long enough to amortise the tapeout — a bet that models stop churning every three months. Llama 3.1 8B is still being served today, years after release, and frontier release cadence has been lengthening. It is a bet with evidence behind it, which is not the same as a safe one.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-we-learned">What We Learned<a href="https://bayrak.dev/blog/taalas-etched-model-silicon#what-we-learned" class="hash-link" aria-label="Direct link to What We Learned" title="Direct link to What We Learned" translate="no">​</a></h2>
<p><strong>What worked:</strong> the derivation. Almost every dramatic number in this story is reproducible from constants that are published — parameter count, HBM bandwidth, TDP, transistor count, die area — and doing that arithmetic first is what separated the three "efficiency" claims from each other. The 299 tok/s ceiling is not Taalas marketing; it falls out of dividing two public specs, and it is <em>why</em> the 17,000 figure is plausible rather than absurd. Likewise the transistor budget closing at 15% weights and 85% everything-else, and HC1's power density landing low in a way consistent with the claim that nothing is moving.</p>
<p><strong>What didn't — and what an independent audit caught.</strong> Our first draft took its Nvidia baselines from secondary coverage instead of from Taalas's own chart: 200 and 350 tok/s where the chart says 230 and 353, which turned a 74× ratio into a claimed 85×. The baselines were not on the page as text; they only exist inside a chart image, and we had not read the image. Three other things did not survive review either: we had attributed the "1000×" claim to the AMD announcement when it is only on Taalas's homepage; we had described the pre-computed-product mechanism as <em>our</em> inference when it is actually circulating in secondary coverage and should have been attributed as such; and we had credited Taalas with a first version built on general-purpose silicon, conflating it with Tenstorrent, which is a different company. All four are now fixed above. The lesson is narrow and repeatable: <strong>when a number only exists inside an image, a text extraction will silently substitute someone else's version of it, and every ratio computed from it inherits the error.</strong></p>
<p><strong>The uncomfortable part:</strong> the design removes one constraint and promotes another. Once nothing is bandwidth-bound, what is left is per-layer serial latency, power delivery, and the reticle limit — and then the deepest constraint of all, which is that the model is frozen. Whether that is a good trade depends on a prediction about how fast models will keep changing, which is a prediction about the world rather than about silicon. The hardware is sound. The bet is the interesting part.</p>
<hr>
<p>The arithmetic in this post is re-derived, not transcribed: every figure is recomputed from the published constants above by <a href="https://github.com/brsbyrk/bayrak.dev/blob/main/scripts/check-taalas-post.py" target="_blank" rel="noopener noreferrer" class="">scripts/check-taalas-post.py</a>, which will fail the build if the numbers drift.</p>]]></content:encoded>
            <category>hardware</category>
            <category>silicon</category>
            <category>inference</category>
            <category>research</category>
        </item>
        <item>
            <title><![CDATA[FlyOS — a Fly Connectome, Five Retractions, and the One Claim That Survived]]></title>
            <link>https://bayrak.dev/blog/flyos-connectome-retention</link>
            <guid>https://bayrak.dev/blog/flyos-connectome-retention</guid>
            <pubDate>Wed, 16 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[We built a deterministic simulator over the Drosophila male CNS connectome — 165,122 neurons and 25.6 million connections — to ask whether its specific wiring does computational work, using randomised rewrites of the real brain as controls. Five of our claims had to be narrowed or withdrawn. The retention result survived parameter jitter, a 30× conduction-delay sweep, and an 8× sweep of the refractory period.]]></description>
            <content:encoded><![CDATA[<p>A fruit fly's central nervous system has been mapped completely: <strong>165,122 neurons and 25,563,197 connections</strong>, every synapse counted. Nobody designed that diagram — evolution did — and the obvious question is whether the specific way those connections are arranged is doing computational work, or whether any network of the same size and density would behave the same. That second half is the hard part. "The wiring matters" is the kind of claim that sounds self-evidently true and is very easy to get wrong, so the project was built around being able to answer it honestly rather than impressively.</p>
<!-- -->
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="why-bother-asking">Why Bother Asking<a href="https://bayrak.dev/blog/flyos-connectome-retention#why-bother-asking" class="hash-link" aria-label="Direct link to Why Bother Asking" title="Direct link to Why Bother Asking" translate="no">​</a></h2>
<p>The dataset is real, published, and awkwardly large in a specific way: the weights table has <strong>151,856,684 rows</strong>, and only <strong>25,563,197</strong> of them survive filtering to neurons whose reconstruction has actually been proofread — <strong>16.8%</strong>. The rest is segmentation debris. FlyOS converts the survivors into a compact binary format (198 MB) with byte-identical reproducibility: two independent implementations, in different languages, agree on every neuron, edge, and synapse count.</p>
<p>On top of that sits a simulator where the same input produces the same output every time, down to a state hash. That is not a nice-to-have. Without it, every comparison below is meaningless, because you cannot tell a structural difference from a random one.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="how-to-ask-it-without-fooling-yourself">How to Ask It Without Fooling Yourself<a href="https://bayrak.dev/blog/flyos-connectome-retention#how-to-ask-it-without-fooling-yourself" class="hash-link" aria-label="Direct link to How to Ask It Without Fooling Yourself" title="Direct link to How to Ask It Without Fooling Yourself" translate="no">​</a></h2>
<p>The honest comparison is not "real brain versus random noise" — that is a strawman that will always be won. It is <strong>random rewrites of the real brain that preserve different amounts of its structure</strong>:</p>
<table><thead><tr><th>Control</th><th>What it preserves</th></tr></thead><tbody><tr><td>Uniform random</td><td>only the neuron and connection counts</td></tr><tr><td>Degree-matched</td><td>+ every neuron's exact in/out degree and every connection's weight</td></tr><tr><td>Sign-preserving</td><td>+ how much excitation vs inhibition flows between cell classes (9 numbers)</td></tr><tr><td>Class-preserving</td><td>+ the full cell-type-to-cell-type connection matrix (330 numbers)</td></tr><tr><td><strong>The real brain</strong></td><td>all of the above, plus everything else</td></tr></tbody></table>
<p>Each rung keeps a bit more of the truth, so instead of one dubious comparison you get a <strong>ladder</strong> — and you can attribute a behaviour to the <em>kind</em> of structure responsible. Five replicates per family, all byte-reproducible from seeds.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-the-ladder-found">What the Ladder Found<a href="https://bayrak.dev/blog/flyos-connectome-retention#what-the-ladder-found" class="hash-link" aria-label="Direct link to What the Ladder Found" title="Direct link to What the Ladder Found" translate="no">​</a></h2>
<p>Ignition threshold rises monotonically as structure is destroyed:</p>
<table><thead><tr><th>Graph</th><th>Threshold</th></tr></thead><tbody><tr><td>Real brain</td><td><strong>0.0064</strong></td></tr><tr><td>Degree-matched</td><td>0.0110</td></tr><tr><td>Structure-free</td><td>0.0173</td></tr></tbody></table>
<p>And the response <em>sharpens</em> at the same time. The real brain behaves like a <strong>dimmer</strong> — raise the input and activity climbs gradually. Every randomised rewrite behaves like a <strong>switch</strong>: dead, then abruptly 97% active. The ordering matters more than any single comparison, and it only exists because there are five rungs rather than one.</p>
<p>Testing whether this was all just excitatory/inhibitory balance <strong>eliminated</strong> that explanation — the sign-preserving control has the same threshold as the degree-matched one. What it revealed instead is that the mechanism splits into independent axes: threshold is set by the coarse cell-type layout, the runaway ceiling by E/I balance, and oscillation strength by fine wiring.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-result-that-stood-out">The Result That Stood Out<a href="https://bayrak.dev/blog/flyos-connectome-retention#the-result-that-stood-out" class="hash-link" aria-label="Direct link to The Result That Stood Out" title="Direct link to The Result That Stood Out" translate="no">​</a></h2>
<p>Feed the network a 20-millisecond pattern of activity, then let it run free. <strong>400 ms later you can still tell which of eight patterns it was — 80 to 95% accurate.</strong> Random rewrites of the same brain, at matched activity: <strong>7.5 to 25%</strong>.</p>
<p>The confound we had to rule out first was mundane: with only 200 stimulated neurons against a 5,000-neuron readout, chance overlap is 6 neurons — 0.121% of dimensions. That cannot produce 80% against a 12.5% ceiling. The decoded state is propagation, not the stimulus.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="five-times-we-were-wrong">Five Times We Were Wrong<a href="https://bayrak.dev/blog/flyos-connectome-retention#five-times-we-were-wrong" class="hash-link" aria-label="Direct link to Five Times We Were Wrong" title="Direct link to Five Times We Were Wrong" translate="no">​</a></h2>
<p>This is the part worth reading carefully, because it is the difference between this and a demo that always works.</p>
<p><strong>One.</strong> We reported that the network's ~108 ms rhythm "requires biological wiring". Then we noticed every comparison had used the <em>same absolute</em> input level, while the graphs ignite at different levels — so the controls were below their own threshold, silent by construction. Compared fairly, the rhythm is near-threshold behaviour <strong>every</strong> graph shows. <em>Retracted.</em></p>
<p><strong>Two.</strong> We reported the real brain's threshold as 2.0× lower than a degree-matched rewrite. A finer measurement grid gave <strong>1.71×</strong>. <em>Corrected.</em></p>
<p><strong>Three.</strong> We reported that only the biological network could <em>encode</em> a stimulus at all. That was our <strong>decoder being too weak</strong> — with common-mode removal and a ridge classifier, the controls reach 100% immediate accuracy. What survives is narrower and more interesting: everyone encodes; <strong>only the real brain still has it 400 ms later.</strong> <em>Retracted.</em></p>
<p><strong>Four.</strong> We hypothesised the memory came from a property of the connection matrix, the standard result in recurrent network theory. <strong>It doesn't.</strong> The prediction missed by 25×, and the ordering inverted: graphs with virtually identical spectral fingerprints — the four degree-preserving families sit within <strong>0.7%</strong> of each other — differ 5× in how well they remember. <em>Hypothesis dead.</em></p>
<p>The pattern is consistent and worth naming: <strong>we kept over-claiming in the direction of "the biology is special", and only better instrumentation caught it.</strong> Not once did a result get stronger under scrutiny.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-test-the-survivor-passed">The Test the Survivor Passed<a href="https://bayrak.dev/blog/flyos-connectome-retention#the-test-the-survivor-passed" class="hash-link" aria-label="Direct link to The Test the Survivor Passed" title="Direct link to The Test the Survivor Passed" translate="no">​</a></h2>
<p>The memory result had an obvious vulnerability. Every neuron had been given <em>identical</em> parameters, and identical units synchronise artificially — so the memory could have been an artefact of that uniformity rather than of the wiring.</p>
<p>So we jittered every neuron's decay and threshold by ±10%, ±20% and ±30% — five runs, each measured at three activity offsets, so fifteen measurements. (Three independent random draws exist only at ±20%.) If it was an artefact, it should collapse.</p>
<table><thead><tr><th>Condition</th><th>~1.0% activity</th><th>~1.7% activity</th><th>~2.3% activity</th></tr></thead><tbody><tr><td>No jitter (original)</td><td>—</td><td>80.0%</td><td>95.0%</td></tr><tr><td>±10%, seed 42</td><td>87.5%</td><td>92.5%</td><td>92.5%</td></tr><tr><td>±20%, seed 42</td><td>80.0%</td><td>90.0%</td><td>90.0%</td></tr><tr><td>±20%, seed 7</td><td>82.5%</td><td>90.0%</td><td>100.0%</td></tr><tr><td>±20%, seed 13</td><td>75.0%</td><td>87.5%</td><td>85.0%</td></tr><tr><td>±30%, seed 42</td><td>67.5%</td><td>95.0%</td><td>95.0%</td></tr></tbody></table>
<p>It survived everything. Then we attacked the other idealisation the project's own assumptions flagged as most distorting: <strong>conduction delay</strong>. Real neurons take time to signal; in this model, signals arrived instantly, which the assumptions recorded as overestimating synchrony.</p>
<table><thead><tr><th>Delay</th><th>Low activity</th><th>Mid activity</th><th>High activity</th></tr></thead><tbody><tr><td>1 ms (original)</td><td>95.0%</td><td>80.0%</td><td>95.0%</td></tr><tr><td>3 ms</td><td>35.0%</td><td>62.5%</td><td>70.0%</td></tr><tr><td>10 ms</td><td>60.0%</td><td>67.5%</td><td>77.5%</td></tr><tr><td>30 ms</td><td>100.0%</td><td>100.0%</td><td>100.0%</td></tr></tbody></table>
<p>Retention survives a 30× range of the assumption most likely to destroy it, and the contrast holds: at 10 ms, the real brain retains 60–77.5% where sign-preserving, class-preserving and degree-matched rewrites reach 7.5–20%, 10–15% and 12.5–15%.</p>
<p>The most interesting thing in that table is a dissociation. <strong>Delay makes encoding trivial for every graph — all of them hit 100% at immediate readout — while conferring none of the persistence.</strong> What biological wiring does is demonstrably not what delay does.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-the-memory-actually-is">What the Memory Actually Is<a href="https://bayrak.dev/blog/flyos-connectome-retention#what-the-memory-actually-is" class="hash-link" aria-label="Direct link to What the Memory Actually Is" title="Direct link to What the Memory Actually Is" translate="no">​</a></h2>
<p>Knowing <em>that</em> the wiring remembers is not knowing <em>how</em>, and a property you can't explain isn't one you can reuse. So we asked the non-statistical question: what does the retained state physically consist of?</p>
<p>Four candidates, each with a distinct signature, fixed before looking: a self-sustaining attractor, a circulating loop, a wave still travelling outward, or a passive decaying echo.</p>
<p>The first answer was a <strong>limit cycle with a 160 ms period</strong>. The pattern-specific state appeared to recur at exact multiples of 160 ms — 160, 320, 480, 640 — at correlation <strong>r = 0.742</strong>, while intermediate lags sat near zero. The story was that the input selects <em>which phase</em> the network occupies, and an attracting cycle holds that phase, so the selection persists instead of fading.</p>
<p>That was a satisfying answer. It was also wrong, and the way it was wrong is the most useful thing in this post.</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-retraction">The retraction<a href="https://bayrak.dev/blog/flyos-connectome-retention#the-retraction" class="hash-link" aria-label="Direct link to The retraction" title="Direct link to The retraction" translate="no">​</a></h3>
<p>We only found it because we varied the <strong>measurement</strong> instead of the network. Every test up to that point changed the graph and left the analysis fixed at one configuration: 20 ms bins, an 800 ms window. The recurrence analysis reported its peak lag in <em>bins</em>, and 8 bins × 20 ms happens to be exactly 160 ms.</p>
<p>So we changed the bin width and nothing else — same graph, same operating point:</p>
<table><thead><tr><th>bin width</th><th>peak lag</th><th>in milliseconds</th></tr></thead><tbody><tr><td>5 ms</td><td>24 bins</td><td><strong>120 ms</strong></td></tr><tr><td>10 ms</td><td>24 bins</td><td><strong>240 ms</strong></td></tr><tr><td>20 ms</td><td>8 bins</td><td>160 ms</td></tr><tr><td>40 ms</td><td>8 bins</td><td><strong>320 ms</strong></td></tr></tbody></table>
<p><strong>A real period is fixed in milliseconds no matter how you bin it. This one isn't.</strong> At 5 ms bins the correlation at 160 ms is +0.272; at 40 ms bins it is −0.039, which is nothing at all. The "comb at exactly 160/320/480/640 ms" was a comb of multiples of the peak lag <em>in bins</em>, which at 20 ms binning lands on multiples of 160 ms. The limit cycle was an artifact of the analysis grid.</p>
<p>The tell was there and we talked ourselves out of it twice. The "period" had already survived a 10× change in the membrane time constant, a 4× change in conduction delay, an 8× change in the refractory period, and an 8× change in network size — without moving. <strong>Nothing physical is invariant to four independent parameters.</strong> A null against a well-established law (delay sets oscillation frequency) is a reason to suspect your instrument, not the law.</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-the-correction-leaves">What the correction leaves<a href="https://bayrak.dev/blog/flyos-connectome-retention#what-the-correction-leaves" class="hash-link" aria-label="Direct link to What the correction leaves" title="Direct link to What the correction leaves" translate="no">​</a></h3>
<p>The <em>contrast</em> was never the artifact — only the period was. Measured at three bin widths with activity matched (biological 2.06%, block-301 2.70%, sign-401 2.44%):</p>
<table><thead><tr><th>bin width</th><th>biological</th><th>block-301</th><th>sign-401</th></tr></thead><tbody><tr><td>5 ms</td><td><strong>+0.564</strong></td><td>+0.025</td><td>+0.097</td></tr><tr><td>20 ms</td><td><strong>+0.746</strong></td><td>+0.011</td><td>+0.136</td></tr><tr><td>40 ms</td><td><strong>+0.796</strong></td><td>+0.005</td><td>+0.140</td></tr></tbody></table>
<p>Only the <em>location</em> of the peak follows the bin grid. The gap over the matched controls — 5× to 159× — holds at every bin width.</p>
<p>So the honest statement is weaker than a limit cycle and better supported: the biological graph's pattern-specific state stays <strong>correlated with itself over hundreds of milliseconds</strong>, and the randomisations decorrelate. That is persistence with structure, not a clock.</p>
<p>It is also a better explanation, for a specific reason. "The input selects which phase of a cycle" was a <em>separate</em> claim from the retention measurement, so it could be wrong on its own. "The state retains its pattern-specific structure" is the same fact the decoder is measuring directly. The mechanism no longer sits on top of the result — it <em>is</em> the result.</p>
<p>One more thing worth knowing: the code is <strong>distributed, not a labelled line</strong>. Five neurons decode at 52.5%, twenty-five at 57.5%, a hundred at 80%, and five hundred saturate at 82.5%. The neurons doing the work sit about one hop from the stimulus — the direct targets — rather than spread across the network.</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="which-half-of-that-is-the-wiring-and-which-is-the-weights">Which half of that is the wiring, and which is the weights?<a href="https://bayrak.dev/blog/flyos-connectome-retention#which-half-of-that-is-the-wiring-and-which-is-the-weights" class="hash-link" aria-label="Direct link to Which half of that is the wiring, and which is the weights?" title="Direct link to Which half of that is the wiring, and which is the weights?" translate="no">​</a></h3>
<p>"Maintains coherence" is a description, not yet an explanation. Two things could be responsible: the <strong>wiring</strong> (which neuron talks to which) or the <strong>weights</strong> (how many synapses on each connection). Every randomised control we'd built so far moved both together, so none of them could tell them apart.</p>
<p>So we built one that moves only the weights — <strong>permuting the synapse counts while leaving the wiring byte-for-byte identical</strong> — and, separately, rewired edges gradually in a ladder.</p>
<p>The result splits the mechanism cleanly in two.</p>
<p><strong>The wiring is surprisingly hard to break.</strong> Rewiring 4% of edges costs essentially nothing. Rewiring <strong>18%</strong> — moving 4.5 million connection endpoints — still costs essentially nothing: decoding 85%. Then between 18% and 63% it falls off a cliff to 25%. The substrate that carries the memory doesn't degrade gracefully, it <strong>fragments</strong>.</p>
<p><strong>The weights decide how much information rides on it.</strong> With the wiring held byte-identical and 78% of the synapse counts reassigned, decoding <strong>halves, 87.5% down to 40%</strong>.</p>
<p>That is the part that corrected our own story: the same wiring, carrying less than half the information. Whatever the retained state is made of, its <em>content</em> depends on how the synapse counts are arranged, not only on which neurons are connected.</p>
<blockquote>
<p><strong>the wiring sets how much can be retained · the weights set how much actually is</strong></p>
</blockquote>
<p><strong>Caveat, stated plainly:</strong> our original version of this section claimed the recurrence signature "survives completely intact, same 160 ms period" under the weight permutation. That half is void now — it was measured with the affected metric, at one bin width. The <em>information</em> half (87.5% → 40%) is a decoding measurement and stands. And the re-run is now done:
at three bin widths, with both graphs at steady state, the recurrence signature survives a
topology-identical weight permutation (0.412 / 0.836 / 0.917 against the biological 0.565 / 0.750 /
0.804) while retention falls from a mean of 78.3% to 44.2%. So the dissociation holds — as a statement
about the recurrence signature, not about a cycle.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="did-the-result-survive-being-tested-properly">Did the result survive being tested properly?<a href="https://bayrak.dev/blog/flyos-connectome-retention#did-the-result-survive-being-tested-properly" class="hash-link" aria-label="Direct link to Did the result survive being tested properly?" title="Direct link to Did the result survive being tested properly?" translate="no">​</a></h2>
<p>Everything above rested on <strong>one</strong> control graph per condition. That is not enough to claim anything —
with n = 1 the minimum achievable p-value is 0.5, and the field's standard is around a thousand
replicates. So we ran it properly.</p>
<p><strong>146 independent randomisations</strong>, all at a matched operating point:</p>
<table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>connectome</td><td><strong>77.5%</strong></td></tr><tr><td>randomisations (n = 146)</td><td><strong>13.7%</strong> mean, range 0–30%</td></tr><tr><td>how many reached the connectome</td><td><strong>0</strong></td></tr><tr><td>closest one</td><td><strong>48 points short</strong></td></tr><tr><td>p</td><td><strong>0.0068</strong></td></tr></tbody></table>
<p>Three independent runs agreed to within a point — 12.9%, 13.5%, 13.7%. The effect is real, and it is
large. Chance is 12.5%, so the randomisations are sitting <em>at</em> chance while the real wiring is six times
above it.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="then-we-went-looking-for-what-does-it-structurally">Then we went looking for what does it structurally<a href="https://bayrak.dev/blog/flyos-connectome-retention#then-we-went-looking-for-what-does-it-structurally" class="hash-link" aria-label="Direct link to Then we went looking for what does it structurally" title="Direct link to Then we went looking for what does it structurally" translate="no">​</a></h2>
<p>This is where the story turns. We had four candidates for a structural property that explains the
difference, and <strong>all four died</strong>:</p>
<table><thead><tr><th>candidate</th><th>what we measured</th><th>result</th></tr></thead><tbody><tr><td>largest strongly-connected component</td><td>flat at 98.9553–98.9662% — not just across randomisations, but in graphs with <strong>no dynamics at all</strong></td><td>dead</td></tr><tr><td>excitatory/inhibitory balance</td><td>flat to four decimal places</td><td>dead</td></tr><tr><td>reciprocity</td><td>held <em>exactly</em> constant while the behaviour collapsed by 98%</td><td>dead</td></tr><tr><td>assortativity</td><td>moved 10% while behaviour fell 98%</td><td>dead</td></tr></tbody></table>
<p>Four structural statistics, all present in the randomisations too. None of them could be what makes the
difference — and that was starting to look like the real answer: <em>maybe no structural statistic can see
it.</em></p>
<p><strong>Then one did.</strong> The <strong>spectral radius of the weighted connectivity</strong> separates the connectome from
every single randomisation by <strong>2.3 to 3.1×</strong>:</p>
<table><thead><tr><th></th><th>spectral radius</th><th>retention</th></tr></thead><tbody><tr><td>connectome</td><td><strong>6031.8</strong></td><td>77.5%</td></tr><tr><td>rewired 4%</td><td>5750.7</td><td>85.0%</td></tr><tr><td>rewired 18%</td><td>4912.2</td><td>85.0%</td></tr><tr><td>rewired 63%</td><td>2591.1</td><td>25.0%</td></tr><tr><td>rewired 98%</td><td>1973.5</td><td>15.0%</td></tr><tr><td>weights shuffled, wiring untouched</td><td>1934.2</td><td>44.0%</td></tr></tbody></table>
<p>That is the first structural quantity in this whole project that detects the difference at all.</p>
<p>And its mechanism turned out to be measurable rather than mysterious: <strong>the connectome aims its heavy
synapses at well-connected targets.</strong> The correlation between an edge's weight and its target's degree is
<strong>+0.063</strong> in the real brain, decays steadily as you rewire it, and is <strong>−0.0003</strong> once you shuffle the
weights. That single correlation tracks the spectral radius almost perfectly.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="and-then-it-failed--in-the-most-useful-possible-way">And then it failed — in the most useful possible way<a href="https://bayrak.dev/blog/flyos-connectome-retention#and-then-it-failed--in-the-most-useful-possible-way" class="hash-link" aria-label="Direct link to And then it failed — in the most useful possible way" title="Direct link to And then it failed — in the most useful possible way" translate="no">​</a></h2>
<p>Two of those graphs have <strong>essentially the same spectral radius</strong>: 1934.2 and 1926.0 — a difference of
<strong>0.4%</strong>.</p>
<p>They retain at <strong>44%</strong> and <strong>12%</strong>.</p>
<p>A <strong>3.7× difference in behaviour between two networks with the same structural number.</strong> So the spectral
radius cannot be what determines the behaviour, no matter how well it correlates.</p>
<p>That failure is the finding:</p>
<blockquote>
<p><strong>The connectome has two independent structural properties.</strong> The spectral radius follows the <em>weight
arrangement</em>. Retention follows the <em>topology</em>. They can be destroyed separately, and when you do, the
two measurements move independently.</p>
</blockquote>
<p>Rewiring breaks both — which is why the spectral radius looked like the answer for a while. Shuffling the
weights with the wiring held byte-identical breaks only the first, and retargets the question: the
weights control how much information rides on the structure, and the structure decides whether it
survives.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="one-number-that-was-wrong-for-a-good-reason">One number that was wrong for a good reason<a href="https://bayrak.dev/blog/flyos-connectome-retention#one-number-that-was-wrong-for-a-good-reason" class="hash-link" aria-label="Direct link to One number that was wrong for a good reason" title="Direct link to One number that was wrong for a good reason" translate="no">​</a></h2>
<p>We also wrote down the wrong mechanism first. Having measured the spectral radius, we explained it as a
"degree-to-weight correlation" — which sounds right and is <strong>false</strong>.</p>
<p>Degree-preserving rewiring swaps <em>whole edges</em>, so every neuron keeps both its number of connections and
its total synaptic weight. That correlation is <strong>exactly invariant by construction</strong> — we measured
<strong>0.7899 for seven different graphs</strong>, identical to four decimal places, while the spectral radius moved
threefold. A quantity that never changes cannot explain one that does.</p>
<p>The correction took ten minutes to test and is now in the record. It is the third time in this project we
caught ourselves stating a mechanism we had not actually measured, and we have written the fix into the
process rather than promising to be more careful: <strong>a mechanism claim must name the quantity it asserts,
and that quantity must appear in a table.</strong> The first version did not. The corrected one does.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-we-learned">What We Learned<a href="https://bayrak.dev/blog/flyos-connectome-retention#what-we-learned" class="hash-link" aria-label="Direct link to What We Learned" title="Direct link to What We Learned" translate="no">​</a></h2>
<p><strong>What worked:</strong> the method. Pre-registering the decision rule meant the answer was decided before the data arrived. The control ladder localised each effect to a specific kind of structure instead of producing one uninterpretable null. Every permutation null sat at chance (11–15% against a 12.5% floor) throughout, which is what makes the low control numbers trustworthy rather than suspect. And running each claim through a <em>deliberate attempt to break it</em> is what separated four retractions from a confident wrong post.</p>
<p><strong>What didn't:</strong> most of our first explanations. Five claims had to be narrowed or withdrawn, and the surviving one is narrower than we originally stated: not "biology encodes and others don't", but "everyone encodes, only the biology retains" — and that retention is not a decaying trace but <strong>persistent, structured evolution of the pattern-specific state</strong>, which the controls show is the difference between remembering and not, since they are just as active. Our first explanation of that persistence — a 160 ms limit cycle — did not survive being measured two different ways.</p>
<p><strong>The one thing we still can't explain:</strong> <em>which</em> structural feature of the wiring sustains the persistence. We now know more than we did — the spectral radius separates the connectome from every randomisation, its mechanism is weight-to-hub targeting, and it is demonstrably <em>not</em> the control parameter. But the quantity that actually carries the memory is still unidentified. Four aggregate statistics and one spectral quantity have been eliminated. That is the honest edge of the result.</p>
<p><strong>The uncomfortable part:</strong> our errors were almost all in one direction. Better instrumentation never strengthened a claim; it only ever weakened one. That is a useful prior to carry: if a result about biological specialness feels satisfying, that is a reason to attack it harder, not to write it up.</p>
<p><strong>What we would not claim:</strong> that this is a fly brain. It is an anatomical connectome plus an assumed dynamical model — no plasticity, no per-edge delays, no gap junctions, no neuromodulation. The retention result is a statement about what this model does with this wiring. Whether it survives morphology-derived delays is the open question, and uniform delays are not a substitute for them.</p>
<hr>
<p>FlyOS is at <a href="https://github.com/brsbyrk/flyos" target="_blank" rel="noopener noreferrer" class="">github.com/brsbyrk/flyos</a> — code, data, and the full experimental record, including every retraction in the commit that made it.</p>]]></content:encoded>
            <category>neuroscience</category>
            <category>rust</category>
            <category>simulation</category>
            <category>research</category>
        </item>
        <item>
            <title><![CDATA[Carnot Update — Graph Routing, a Fixed Schedule, and a Negative Result]]></title>
            <link>https://bayrak.dev/blog/carnot-graph-routing</link>
            <guid>https://bayrak.dev/blog/carnot-graph-routing</guid>
            <pubDate>Sat, 12 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Chapter II of Carnot: does a sparse graph-routing kernel beat a dense model at matched budget? It lost at every context length — and building the follow-up uncovered a structural defect that explained why the routing rule contributed nothing: it never observed the data.]]></description>
            <content:encoded><![CDATA[<p>The <a class="" href="https://bayrak.dev/blog/carnot-gpu-rans">July post</a> archived Carnot's compressor and closed with a wall: beating gzip by 14% is nice, but the remaining ~38% gap to PAQ and CMIX needs <strong>long-range context modeling</strong> — a learned representation, not a better entropy coder. Chapter II went after that. It ended in a negative result, twice over, and the way it failed turned out to be more interesting than the hypothesis.</p>
<!-- -->
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-hypothesis">The Hypothesis<a href="https://bayrak.dev/blog/carnot-graph-routing#the-hypothesis" class="hash-link" aria-label="Direct link to The Hypothesis" title="Direct link to The Hypothesis" translate="no">​</a></h2>
<p>One sentence, one metric, falsifiable:</p>
<blockquote>
<p>At matched parameter count and matched compute, a sparse graph-routing model predicts byte streams at lower bits/byte than a dense model of the same budget.</p>
</blockquote>
<p>The appeal of routing is that it <em>should</em> be cheap. A dense transformer spends a fixed amount of compute per byte forever. A router picks a path through a neighbourhood and does small local work, so most of the model is dormant most of the time.</p>
<p>The mechanism: 32 stations arranged in a graph, and a puck that visits them. Each station carries an anchor (where it sits), a force vector (which way it pushes), a 16×16 context matrix, and a fixed byte-level instruction. The puck has position and velocity, and momentum — <code>v = 0.5·v + 0.5·F</code>. At each step it scores its four neighbours by <strong>alignment times reachability</strong>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">G(m) = cos(F_m, v) · exp(−‖a_m − p‖² / 2σ²)</span><br></div></code></pre></div></div>
<p>and takes the <code>argmax</code>. Hard selection, geometric scoring — deliberately <em>not</em> a softmax, because the point of the experiment was to test whether discrete routing pays for itself. A shared head then reads the puck's context state and emits the next-byte distribution. Arm A: 34,624 parameters. Arm B: a parameter-matched dense transformer, 33,184 parameters, 4% smaller.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="it-lost">It Lost<a href="https://bayrak.dev/blog/carnot-graph-routing#it-lost" class="hash-link" aria-label="Direct link to It Lost" title="Direct link to It Lost" translate="no">​</a></h2>
<p>Routing was beaten at every context length, and the margin <strong>grew</strong> with context:</p>
<table><thead><tr><th style="text-align:right">Context</th><th style="text-align:right">Routing</th><th style="text-align:right">Dense</th><th style="text-align:right">Δ</th></tr></thead><tbody><tr><td style="text-align:right">256</td><td style="text-align:right">3.8564</td><td style="text-align:right">3.5397</td><td style="text-align:right">+0.3168</td></tr><tr><td style="text-align:right">512</td><td style="text-align:right">3.8776</td><td style="text-align:right">3.4808</td><td style="text-align:right">+0.3968</td></tr><tr><td style="text-align:right">1024</td><td style="text-align:right">3.9135</td><td style="text-align:right">3.4464</td><td style="text-align:right"><strong>+0.4671</strong></td></tr></tbody></table>
<p>Dense also won on <strong>equal wall-clock</strong> — and that is the harder test, because at 1024 context the transformer costs <strong>8.4× the FLOPs per byte</strong>. It won anyway, while paying for its own compute.</p>
<p>Worth saying plainly: both arms are worse than gzip (<strong>2.846</strong> bits/byte). At ~35,000 parameters these models are far too small to be competitive compressors. This was never a product; it was a question about representation.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="we-removed-the-special-parts">We Removed the Special Parts<a href="https://bayrak.dev/blog/carnot-graph-routing#we-removed-the-special-parts" class="hash-link" aria-label="Direct link to We Removed the Special Parts" title="Direct link to We Removed the Special Parts" translate="no">​</a></h2>
<p>The decisive evidence wasn't the loss — it was the ablations. If routing's distinctive machinery is load-bearing, removing it should hurt. We replaced each defining feature and measured at 10 MB, three seeds:</p>
<table><thead><tr><th>Ablation</th><th style="text-align:right">Δ bpb</th><th style="text-align:right">Noise threshold</th><th></th></tr></thead><tbody><tr><td>Momentum removed (β = 1)</td><td style="text-align:right">+0.0120</td><td style="text-align:right">0.0754</td><td>NOISE</td></tr><tr><td>Hardness removed (soft routing)</td><td style="text-align:right">+0.0349</td><td style="text-align:right">0.0754</td><td>NOISE</td></tr><tr><td>Per-node operators → predictors</td><td style="text-align:right">−0.0513</td><td style="text-align:right">0.0754</td><td>NOISE</td></tr></tbody></table>
<p><strong>Nothing moved.</strong> Removing the momentum, the hard selection, and the per-node instructions changed nothing measurable. If you can unscrew the engine and the car drives identically, the engine wasn't connected. The kernel was decoration; a shared emission head was doing all the work.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="then-we-found-the-bug">Then We Found the Bug<a href="https://bayrak.dev/blog/carnot-graph-routing#then-we-found-the-bug" class="hash-link" aria-label="Direct link to Then We Found the Bug" title="Direct link to Then We Found the Bug" translate="no">​</a></h2>
<p>Chapter II's follow-up asked whether that result was really about per-node <strong>capacity</strong> rather than routing at all — by varying the one thing the first experiment never varied: how much per-node compute exists. That experiment added a <em>manipulation check</em>, which is a fancy name for a simple idea: <strong>before you interpret an ablation's null result, prove the ablated thing actually ran.</strong> A null from a mechanism that never fired means "it never ran", not "it didn't help".</p>
<p>The check failed immediately, for a reason nobody expected:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">'the cat sat on the mat'   -&gt; node path 0, 2, 10, 11, 19, 12, 10, 11, 19, ...</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">'ZZZZZZZZZZZZZZZZZZZZZZ'   -&gt; IDENTICAL</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">22 random bytes            -&gt; IDENTICAL</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">8 walkers reading 8 DIFFERENT byte streams -&gt; distinct paths: 1</span><br></div></code></pre></div></div>
<p><strong>The routing rule never observed the data.</strong> Its score consumed only the graph, the anchors, the forces, the position and the velocity — none of which depend on the input. Every byte the model read went into the <em>context state</em>, which reached the emission head and nothing else. The puck's path through the graph was a pure function of its parameters: a <strong>fixed schedule</strong>, pre-computed, the same for a love letter and random noise.</p>
<p>That reframes the whole first experiment. Routing wasn't weak — it was a fixed wiring wearing routing's clothes. The momentum and hardness ablations were noise because there was nothing for them to contribute: the path carried <strong>zero information about the input</strong>. The verdict was right, but my explanation of it was wrong, and the correction is now in the repo.</p>
<p>It also explains the coverage numbers. A fixed schedule visits 12 of 32 stations, so only 10 of 16 instructions ever executed, and the counting instruction — the one the whole follow-up rested on — lived on two stations the walk never reached.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="making-it-real">Making It Real<a href="https://bayrak.dev/blog/carnot-graph-routing#making-it-real" class="hash-link" aria-label="Direct link to Making It Real" title="Direct link to Making It Real" translate="no">​</a></h2>
<p>The fix was small. Make the puck's heading depend on what it has read:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">u = v + W·c          W is 16×16, +256 parameters</span><br></div></code></pre></div></div>
<p><code>c</code> is the context state — the only thing on the walk that carries history, and by the time routing happens it already contains the current byte. Momentum is kept, the geometric scoring is kept, the hard <code>argmax</code> is kept. Only the <em>direction</em> the alignment is measured against changes.</p>
<p>The result was unambiguous. Same model, same everything else:</p>
<table><thead><tr><th></th><th style="text-align:right">Fixed schedule</th><th style="text-align:right">Data-dependent</th></tr></thead><tbody><tr><td>Stations reached</td><td style="text-align:right">12 / 32</td><td style="text-align:right"><strong>26 – 32 / 32</strong></td></tr><tr><td>Instructions executed</td><td style="text-align:right">10 / 16</td><td style="text-align:right"><strong>all 14 real ones</strong></td></tr><tr><td>Counting table</td><td style="text-align:right">identically zero</td><td style="text-align:right"><strong>85 – 225</strong></td></tr><tr><td>Paths differ by input</td><td style="text-align:right">no</td><td style="text-align:right"><strong>yes, from the first step</strong></td></tr></tbody></table>
<p>Those are ranges across six seeds, not single readings: with a fixed schedule the walk deterministically reaches 12 stations, and once routing depends on the data it reaches 26–32 depending on how the route trains. The 15th and 16th instructions are no-ops, so "all real ones" is the honest bar — the no-op occasionally goes unreached, which cannot matter by definition.</p>
<p>For the first time, the channel was genuinely alive.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="it-still-lost">It Still Lost<a href="https://bayrak.dev/blog/carnot-graph-routing#it-still-lost" class="hash-link" aria-label="Direct link to It Still Lost" title="Direct link to It Still Lost" translate="no">​</a></h2>
<p>The follow-up was a 2×2 factorial: a rich 16-instruction set versus the <strong>same model with every instruction replaced by a no-op</strong>, crossed with two ways of reading the instructions' output. 1 MB of enwik8, three seeds. If instructions matter, the rich arm should beat the do-nothing arm.</p>
<table><thead><tr><th>Contrast</th><th style="text-align:right">Δ bpb</th><th style="text-align:right">Noise threshold</th><th></th></tr></thead><tbody><tr><td><strong>16 live instructions vs all no-ops</strong></td><td style="text-align:right"><strong>+0.0014</strong></td><td style="text-align:right">0.1009</td><td><strong>NOISE</strong></td></tr><tr><td>Wide read-out vs averaged</td><td style="text-align:right">+0.0242</td><td style="text-align:right">0.0974</td><td>NOISE</td></tr><tr><td>Interaction (the pre-registered prediction)</td><td style="text-align:right">−0.0154</td><td style="text-align:right">0.1085</td><td>NOISE</td></tr></tbody></table>
<p><strong>+0.0014 bits/byte against a 0.10 threshold.</strong> Sixteen working instructions, including a counting table that demonstrably filled, are indistinguishable from instructions that do nothing. The pre-registered prediction — that richer instructions would only help with a wider read-out — failed too.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-we-learned">What We Learned<a href="https://bayrak.dev/blog/carnot-graph-routing#what-we-learned" class="hash-link" aria-label="Direct link to What We Learned" title="Direct link to What We Learned" translate="no">​</a></h2>
<p><strong>What worked:</strong> the experimental method, eventually. Pre-registering the kill criterion meant the answer was decided before the data arrived. The manipulation check — added specifically because the first experiment's nulls were uninterpretable — found a structural defect on its first use, before 12 runs were spent on a dead channel. And each stage removed an alternative explanation: it wasn't the routing rule, wasn't the instruction set, wasn't the read-out width, and wasn't the context window.</p>
<p><strong>What didn't:</strong> the idea. At ~35,000 parameters, the shared emission head reading a 16-dimensional recurrent state accounts for essentially all of the model's behaviour. The graph structure, the geometric routing, the per-node instructions — none of it contributes anything measurable on top.</p>
<p><strong>The uncomfortable part:</strong> the sequence was <em>null → find a defect → fix the defect → still null</em>. That is a much stronger negative than the first one, because the mechanism was verified working when it produced the null. It's also a reminder that a negative result is only as good as your ability to show the mechanism ran.</p>
<p><strong>The one lever never touched:</strong> scale. Everything here ran at tens of thousands of parameters on 1–10 MB. That constraint is capacity, not architecture, and it is the honest reason both arms sit above gzip. Testing it needs real compute and a different budget — so this is where Chapter II stops.</p>
<p>Chapter II is closed as a negative result, and the negative is documented to the same standard as a win would have been: the design, the defect, the fix, the numbers, and every ablation that failed to matter. Three experiments, two code revisions, all on CPU.</p>
<hr>
<p>Carnot is open-source under MIT. <a href="https://github.com/brsbyrk/carnot" target="_blank" rel="noopener noreferrer" class="">github.com/brsbyrk/carnot</a></p>]]></content:encoded>
            <category>compression</category>
            <category>research</category>
            <category>machine-learning</category>
        </item>
        <item>
            <title><![CDATA[Carnot Update — GPU rANS, CubeCL Kernels, and Multi-threaded BWT]]></title>
            <link>https://bayrak.dev/blog/carnot-gpu-rans</link>
            <guid>https://bayrak.dev/blog/carnot-gpu-rans</guid>
            <pubDate>Thu, 30 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[One month of Carnot: a 50-100× faster rANS coder replacing PAQ's arithmetic coder, a GPU rANS kernel on CubeCL targeting Metal/Vulkan/CUDA/DX12, GPU BWT via prefix doubling, VMBC extensions, and final benchmark results.]]></description>
            <content:encoded><![CDATA[<p>The <a class="" href="https://bayrak.dev/blog/carnot-compression-research">June post</a> left Carnot with a working BWT pipeline, a bytecode VM, and a GPU BWT scaffold. The open question was whether entropy coding — the final stage after transforms — could be both fast and precise. This month we answered it, ported the entropy layer to the GPU, pushed BWT onto the GPU via prefix doubling, added multi-threading, and extended the VM's pattern detectors. Here's what happened and what we learned.</p>
<!-- -->
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="rans-replacing-the-arithmetic-coder">rANS: Replacing the Arithmetic Coder<a href="https://bayrak.dev/blog/carnot-gpu-rans#rans-replacing-the-arithmetic-coder" class="hash-link" aria-label="Direct link to rANS: Replacing the Arithmetic Coder" title="Direct link to rANS: Replacing the Arithmetic Coder" translate="no">​</a></h2>
<p>The original pipeline ended with an order-1 arithmetic coder from PAQ. It was accurate but slow — arithmetic coding maintains a high-precision interval and rescales it per symbol, an inherently sequential process. <strong>rANS</strong> (range Asymmetric Numeral Systems) replaces interval arithmetic with a single integer state update: divide by the symbol frequency, multiply by the total, add the cumulative offset. Division and multiplication instead of interval logic — <strong>streaming-friendly</strong> and far faster.</p>
<p><code>carnot-rans</code> is a pure-Rust crate with order-0 and order-1 adaptive models. Frequencies update as symbols are encoded, so the model tightens over the stream. The key trick is <strong>16× interleaving</strong>: 16 independent rANS states encode interleaved symbols, breaking the data dependency chain and enabling pipelined throughput. Each symbol lands on a different state in a stride of 16; decoding reads them back in the same order — no parallel hazard, just ILP-friendly sequential throughput. On enwik8, rANS encodes at 180-220 MB/s and decodes at 350-400 MB/s — <strong>50-100× faster</strong> than the PAQ arithmetic coder, with identical compression ratios.</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="gpu-rans-kernel">GPU rANS Kernel<a href="https://bayrak.dev/blog/carnot-gpu-rans#gpu-rans-kernel" class="hash-link" aria-label="Direct link to GPU rANS Kernel" title="Direct link to GPU rANS Kernel" translate="no">​</a></h3>
<p>Can rANS run on a GPU? Encoding is symbol-parallel (nice for GPUs) but decoding is inherently sequential — you can't decode symbol N+1 until you know state N. We split the problem: histogram on GPU → table build on CPU → encode on GPU → decode on CPU. The kernel uses <strong><a href="https://github.com/tracel-ai/cubecl" target="_blank" rel="noopener noreferrer" class="">CubeCL</a></strong> 0.10, a Rust GPU compute framework targeting wgpu — one codebase, four backends (Metal, Vulkan, CUDA, DX12). Each workgroup builds a local histogram before atomically merging into global counts, and the encode pass scatters encoded symbols into a global output buffer. On a 1MB input, the GPU encode pass takes ~0.5ms; PCIe transfer is the real bottleneck.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="gpu-bwt-via-prefix-doubling">GPU BWT via Prefix Doubling<a href="https://bayrak.dev/blog/carnot-gpu-rans#gpu-bwt-via-prefix-doubling" class="hash-link" aria-label="Direct link to GPU BWT via Prefix Doubling" title="Direct link to GPU BWT via Prefix Doubling" translate="no">​</a></h2>
<p>The June BWT scaffold was CUDA-only. To go cross-platform, we implemented <strong>prefix doubling + bitonic sort</strong> in CubeCL: assign each suffix an initial rank from its first character, repeatedly double the comparison window, and sort rank pairs with bitonic sort over log₂(n) iterations. The bitonic sort maps perfectly to GPU workgroups — it's a fixed comparison network with no branching, ideal for SIMD lanes.</p>
<p>The approach works correctly. The crossover point tells the whole story:</p>
<table><thead><tr><th style="text-align:right">Input size</th><th style="text-align:center">CPU SA-IS</th><th style="text-align:center">GPU</th></tr></thead><tbody><tr><td style="text-align:right">16 KB</td><td style="text-align:center">0.15 ms</td><td style="text-align:center">1.8 ms</td></tr><tr><td style="text-align:right">256 KB</td><td style="text-align:center">3.2 ms</td><td style="text-align:center">4.1 ms</td></tr><tr><td style="text-align:right">1 MB</td><td style="text-align:center">14 ms</td><td style="text-align:center">7.8 ms</td></tr><tr><td style="text-align:right">100 MB</td><td style="text-align:center">1.8 s</td><td style="text-align:center">1.4 s</td></tr></tbody></table>
<p>In <strong>release mode</strong>, CPU SA-IS is so fast that GPU doesn't win until ~1MB. For Carnot's 64MB blocks, CPU is faster. The kernel is a working reference, not a practical accelerator — we're archiving it as such.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="multi-threaded-bwt">Multi-threaded BWT<a href="https://bayrak.dev/blog/carnot-gpu-rans#multi-threaded-bwt" class="hash-link" aria-label="Direct link to Multi-threaded BWT" title="Direct link to Multi-threaded BWT" translate="no">​</a></h2>
<p>While the GPU path matured, we parallelized the CPU path. <code>carnot-bwt</code> uses <strong><a href="https://github.com/IlyaGrebnov/libsais" target="_blank" rel="noopener noreferrer" class="">libsais-rs</a></strong> with <strong>rayon</strong> for block-level parallelism, controlled by <code>CARNOT_BWT_THREADS</code>. Each 64MB block gets its own SA-IS invocation on a dedicated thread, with multi-block pipelines overlapping BWT, MTF, and rANS stages. On a 4-core machine: <strong>2.5× speedup</strong> over single-threaded, no ratio penalty.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="vmbc-extensions">VMBC Extensions<a href="https://bayrak.dev/blog/carnot-gpu-rans#vmbc-extensions" class="hash-link" aria-label="Direct link to VMBC Extensions" title="Direct link to VMBC Extensions" translate="no">​</a></h2>
<p>The bytecode VM gained two new pattern detectors. The <strong>arithmetic progression detector</strong> recognizes sequences like <code>0, 5, 10, 15, ...</code> and encodes them as a single <code>(start, step, count)</code> program — a 1MB arithmetic sequence compresses to ~30 bytes, roughly 9× smaller than zstd on this class of data. The <strong>counting pattern detector</strong> extends this to nested arithmetic progressions, where each "digit" follows its own step within a wrapping modulus (think base-N counters with varying stride per position). These are niche wins — dramatic on synthetic structured data, zero contribution to natural text. That's the VM's trade: high-ceiling, narrow-domain compression.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="final-benchmarks">Final Benchmarks<a href="https://bayrak.dev/blog/carnot-gpu-rans#final-benchmarks" class="hash-link" aria-label="Direct link to Final Benchmarks" title="Direct link to Final Benchmarks" translate="no">​</a></h2>
<p>Carnot is now a multi-block, multi-threaded, rANS-backed BWT compressor. We dropped RePair from the default pipeline — MTF + zero-run captures enough digram redundancy on the rANS backend that RePair's overhead stopped paying for itself.</p>
<table><thead><tr><th>Compressor</th><th style="text-align:center">enwik8 1MB</th><th style="text-align:center">enwik8 100MB</th></tr></thead><tbody><tr><td><strong>Carnot (BWT+MTF+rANS)</strong></td><td style="text-align:center"><strong>0.305</strong></td><td style="text-align:center">0.241</td></tr><tr><td>gzip -9</td><td style="text-align:center">0.354</td><td style="text-align:center">0.365</td></tr><tr><td>bzip2 -9</td><td style="text-align:center">0.310</td><td style="text-align:center">0.290</td></tr><tr><td>xz -9e</td><td style="text-align:center">—</td><td style="text-align:center">0.248</td></tr></tbody></table>
<p>At 1MB, Carnot beats gzip by 14%. At 100MB, it edges past bzip2 and lands within 3% of LZMA2 — but 4× faster than the June PAQ-based pipeline. The GPU pipeline achieves <strong>identical ratios</strong> to CPU when MTF is active (0.40 vs 0.40 on enwik8 1MB). The transforms dominate; the entropy coder faithfully captures what they produce.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-we-learned">What We Learned<a href="https://bayrak.dev/blog/carnot-gpu-rans#what-we-learned" class="hash-link" aria-label="Direct link to What We Learned" title="Direct link to What We Learned" translate="no">​</a></h2>
<p><strong>What worked:</strong> rANS is the right entropy coder — fast, precise, and interleaving eliminates the sequential bottleneck for all practical purposes. CubeCL delivered cross-platform GPU compute with one codebase and no CUDA dependency. Multi-threaded BWT scales cleanly with libsais + rayon. The VM pattern detectors are real wins for structured data, even if they don't move the needle on text benchmarks.</p>
<p><strong>What didn't:</strong> GPU BWT via prefix doubling can't beat a tuned SA-IS on CPU for realistic block sizes — the GPU's advantage is throughput on huge inputs, not latency on 64MB chunks. GPU rANS decode remains CPU-bound; the sequential dependency means the kernel only helps on the encode side, and PCIe transfer eats most of that gain on small-to-medium inputs.</p>
<p><strong>Why we're archiving:</strong> Carnot set out to explore how close we could get to the true compression limit by trading time for ratio, with GPUs as a speculative accelerator. We found a solid answer: BWT + MTF + rANS gets within 3% of xz with a clean, maintainable Rust codebase. The GPU work proved the approach viable but not yet practical at these block sizes. The remaining ~38% gap to PAQ/CMIX requires long-range context modeling — a different problem, for a different project.</p>
<p>The repo stays open. The dead ends are documented. The code compiles, benchmarks run, and anyone curious about compression, GPU compute, or rANS can pick up where we left off.</p>
<hr>
<p>Carnot is open-source under MIT. <a href="https://github.com/brsbyrk/carnot" target="_blank" rel="noopener noreferrer" class="">github.com/brsbyrk/carnot</a></p>]]></content:encoded>
            <category>rust</category>
            <category>compression</category>
            <category>gpu</category>
            <category>research</category>
        </item>
        <item>
            <title><![CDATA[Carnot — Compression, GPUs, and Cryptographic Accumulators]]></title>
            <link>https://bayrak.dev/blog/carnot-compression-research</link>
            <guid>https://bayrak.dev/blog/carnot-compression-research</guid>
            <pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A deep dive into Carnot: pattern-scale decomposition that beats zstd by 9×, a BWT pipeline that outruns lzma on enwik8, GPU-accelerated transforms, and a bilinear accumulator on BLS12-381.]]></description>
            <content:encoded><![CDATA[<p><a href="https://en.wikipedia.org/wiki/A_Mathematical_Theory_of_Communication" target="_blank" rel="noopener noreferrer" class="">Claude Shannon</a> proved in 1948 that every data source has a theoretical minimum — you can't compress below Shannon entropy. But that limit applies to the <em>true</em> data source, not your file. Real-world data is far more structured than byte frequencies suggest, and standard compressors leave a lot on the table.</p>
<!-- -->
<p><strong>Carnot</strong> is an open research project named after <a href="https://en.wikipedia.org/wiki/Nicolas_L%C3%A9onard_Sadi_Carnot" target="_blank" rel="noopener noreferrer" class="">Sadi Carnot</a> — who defined the theoretical maximum efficiency of heat engines. The project asks the same question in a different domain: <em>how close can we get to the true compression limit if we stop caring about throughput?</em></p>
<p>It has grown into three tracks across a Rust workspace: pattern-scale decomposition compression, GPU-accelerated transforms, and a cryptographic accumulator on BLS12-381. It's a lab notebook, not a product — 21 experiments so far, with failures documented as carefully as successes.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="pattern-scale-decomposition">Pattern-Scale Decomposition<a href="https://bayrak.dev/blog/carnot-compression-research#pattern-scale-decomposition" class="hash-link" aria-label="Direct link to Pattern-Scale Decomposition" title="Direct link to Pattern-Scale Decomposition" translate="no">​</a></h2>
<p>The key insight, formalized in <a href="https://github.com/brsbyrk/carnot/blob/main/docs/cf-unification.md" target="_blank" rel="noopener noreferrer" class=""><code>docs/cf-unification.md</code></a>: every structured byte sequence decomposes into two orthogonal components — the <em>pattern</em> (generating rule: constant, alternating, counting, repeating) and the <em>scale</em> (length, repeats, frequency). Standard compressors conflate both channels; Carnot separates them.</p>
<p>The math uses continued fraction rational approximations of byte sequences. CF convergents find the infinite-limit structure in small terms, but encode length implicitly through a huge final term costing O(n). The bytecode VM is the practical fix — same pattern detection, but with an explicit O(log n) scale channel.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-bytecode-vm">The Bytecode VM<a href="https://bayrak.dev/blog/carnot-compression-research#the-bytecode-vm" class="hash-link" aria-label="Direct link to The Bytecode VM" title="Direct link to The Bytecode VM" translate="no">​</a></h2>
<p>A 17-opcode stack VM with loop stack encodes structural patterns as tiny programs approaching the Kolmogorov complexity floor — the shortest possible description of the data:</p>
<table><thead><tr><th>Op</th><th>Pattern</th><th>Program size</th><th>100KB compressed</th><th>vs zstd</th></tr></thead><tbody><tr><td>LOOP/OUTPUT</td><td>Constant byte</td><td>7 B</td><td>~17 B</td><td><strong>~6×</strong></td></tr><tr><td>LOOP/PUSH/OUTPUT</td><td>Alternating AB</td><td>10 B</td><td>~30 B</td><td><strong>~9×</strong></td></tr><tr><td>Nested loop + math</td><td>Counting 0..255</td><td>15 B</td><td><strong>74 B</strong></td><td><strong>3.8×</strong></td></tr><tr><td>REPEAT_BLOCK</td><td>Repeating block</td><td>5+len</td><td>445 B</td><td>—</td></tr></tbody></table>
<p>Programs are discovered via beam search (width 50, sampling 4KB windows) rather than exhaustive enumeration. The adaptive pipeline selector auto-routes between 7 strategies: random pass-through, delta encoding for sorted integers, BWT+MTF+RLE for text, sparse encoding for low-entropy data, dedup for repeated blocks, RePair grammar compression, and the VM bytecode path for periodic/structured input. Candidate pruning gives a 1070× speedup on uniform data.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-bwt-pipeline">The BWT Pipeline<a href="https://bayrak.dev/blog/carnot-compression-research#the-bwt-pipeline" class="hash-link" aria-label="Direct link to The BWT Pipeline" title="Direct link to The BWT Pipeline" translate="no">​</a></h2>
<p>The best general-purpose result comes from a <strong>five-stage Burrows-Wheeler pipeline</strong>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Raw data → BWT (SA-IS) → Move-to-Front → Zero-run → RePair → Order-1 Arithmetic Coding</span><br></div></code></pre></div></div>
<p>Each stage targets a different redundancy type:</p>
<table><thead><tr><th>Stage</th><th>What it exploits</th><th style="text-align:center">Gain</th></tr></thead><tbody><tr><td>BWT (sentinel SA-IS)</td><td>Long-range character grouping</td><td style="text-align:center">~25%</td></tr><tr><td>MTF</td><td>Local frequency skew after BWT</td><td style="text-align:center">~10%</td></tr><tr><td>Zero-run</td><td>Long runs of MTF-0 values</td><td style="text-align:center">~5%</td></tr><tr><td>RePair (incremental)</td><td>Repeated digrams</td><td style="text-align:center">~15%</td></tr><tr><td>Order-1 AC</td><td>Byte-level conditional probability</td><td style="text-align:center">~6.5%</td></tr></tbody></table>
<p>The evolution is visible in the experiment log — five attempts to get from a Python prototype at 0.2971 ratio to the Rust champion. The breakthrough was sentinel-based SA-IS BWT + incremental RePair, refined from 0.2512 ratio and 29 minutes down to 0.2352 at 2 minutes via flat-array order-1 modeling.</p>
<p>On <a href="https://mattmahoney.net/dc/textdata.html" target="_blank" rel="noopener noreferrer" class="">enwik8</a> 100MB:</p>
<table><thead><tr><th>Compressor</th><th style="text-align:center">Ratio</th><th>Time</th></tr></thead><tbody><tr><td><strong>Carnot BWT</strong></td><td style="text-align:center"><strong>0.2352</strong></td><td>2m 07s</td></tr><tr><td>PPMd</td><td style="text-align:center">0.225</td><td>—</td></tr><tr><td>xz -9e</td><td style="text-align:center">0.2483</td><td>82s</td></tr><tr><td>zstd -22</td><td style="text-align:center">0.2795</td><td>65s</td></tr><tr><td>bzip2 -9</td><td style="text-align:center">0.2901</td><td>~1s</td></tr><tr><td>gzip -9</td><td style="text-align:center">0.3648</td><td>~1s</td></tr></tbody></table>
<p>At 1GB (10× concatenated enwik8), xz pulls ahead — 0.1737 vs Carnot's 0.2403 — because LZMA2's 64MB sliding window exploits cross-copy repetition that BWT, confined to each 64MB block, misses. Carnot wins on single files by 5.3%; xz wins on concatenated copies. Both approaches have their regime.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="multi-threading-and-speed-improvements">Multi-threading and Speed Improvements<a href="https://bayrak.dev/blog/carnot-compression-research#multi-threading-and-speed-improvements" class="hash-link" aria-label="Direct link to Multi-threading and Speed Improvements" title="Direct link to Multi-threading and Speed Improvements" translate="no">​</a></h2>
<p>Multi-threaded BWT gives 2.6× speedup at a 3.8% ratio cost on 100MB enwik8. Multi-block parallel compression scales near-linearly at 4×. The whole pipeline processes 64MB blocks sequentially to stay within RAM limits — SA-IS suffix array on 1GB needs ~5GB for the SA alone. A production run would need either 32GB+ RAM, out-of-core BWT, or pre-deduplication.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="gpu-bwt-acceleration">GPU BWT Acceleration<a href="https://bayrak.dev/blog/carnot-compression-research#gpu-bwt-acceleration" class="hash-link" aria-label="Direct link to GPU BWT Acceleration" title="Direct link to GPU BWT Acceleration" translate="no">​</a></h2>
<p>BWT suffix array construction is the pipeline's bottleneck. <code>carnot-gpu-core</code> offloads it to the GPU via <a href="https://github.com/IlyaGrebnov/libcubwt" target="_blank" rel="noopener noreferrer" class="">libcubwt</a>, targeting a GTX 1060 6GB (Pascal, CC 6.1, CUDA 12.2). The FFI is scaffolded with <code>cudarc</code> device management and VRAM budgeting — libcubwt needs 20.5n bytes for n-byte input, capping at ~300MB on the 6GB card. A CPU fallback runs suffix sorting directly for testing. The <code>carnot-gpu-cli</code> provides <code>info</code> and <code>bench-bwt</code> subcommands.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="bilinear-accumulator-on-bls12-381">Bilinear Accumulator on BLS12-381<a href="https://bayrak.dev/blog/carnot-compression-research#bilinear-accumulator-on-bls12-381" class="hash-link" aria-label="Direct link to Bilinear Accumulator on BLS12-381" title="Direct link to Bilinear Accumulator on BLS12-381" translate="no">​</a></h2>
<p>The <code>carnot-accumulator</code> crate implements a bilinear accumulator for constant-size set membership proofs — 48 bytes for the accumulator and each witness, regardless of how many elements are in the set:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Setup:  s = random secret</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        pk = g₂^s</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Accumulate:  acc = g₁^{∏(s + hash(x_i))}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Witness:     w   = g₁^{∏_{i≠j}(s + hash(x_j))}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Verify:      e(w, g₂^{hash(x_j)} · pk) == e(acc, g₂)</span><br></div></code></pre></div></div>
<p>Key implementation details:</p>
<ul>
<li class="">Pairings via <code>blst</code> on the BLS12-381 curve</li>
<li class=""><strong>NTT-based polynomial multiplication</strong> — tree-based product expansion in O(n log n) over a field with 2^32 roots of unity</li>
<li class=""><strong>Batch witness generation</strong> via prefix-suffix products in O(N)</li>
<li class=""><strong>Merkle tree</strong> integration for byte-level membership — each leaf is SHA-256(index || byte), so a verifier can brute-force all 256 byte values against a single Merkle proof</li>
<li class="">Powers-of-tau trusted setup with chunked verification and parallel brute-force checking</li>
</ul>
<p>This isn't directly about compression — it's infrastructure for verifiable computation. The polynomial math (NTT, pairings, finite fields) connects both domains.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="what-didnt-work">What Didn't Work<a href="https://bayrak.dev/blog/carnot-compression-research#what-didnt-work" class="hash-link" aria-label="Direct link to What Didn't Work" title="Direct link to What Didn't Work" translate="no">​</a></h2>
<p>Failures are preserved as terrain maps for future explorers:</p>
<table><thead><tr><th>Experiment</th><th>Why it failed</th></tr></thead><tbody><tr><td>Renormalization group compression</td><td>Data Processing Inequality: provably bounded by H₁</td></tr><tr><td>Byte embeddings (bilinear)</td><td>Factorization loses specific transitions; simple counters win</td></tr><tr><td>Grammar induction (Sequitur)</td><td>Rule explosion on real data</td></tr><tr><td>Mixture of Experts</td><td>Gradient bug; hard assignment fragments data</td></tr><tr><td>Chain transform (Follow)</td><td>99.98% collision rate on text</td></tr><tr><td>CF compression</td><td>Confirmed negative — does not actually compress</td></tr><tr><td>Sorted u32 delta</td><td>Byte-level delta inflates; needs word-level encoding</td></tr></tbody></table>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="the-structural-gap">The Structural Gap<a href="https://bayrak.dev/blog/carnot-compression-research#the-structural-gap" class="hash-link" aria-label="Direct link to The Structural Gap" title="Direct link to The Structural Gap" translate="no">​</a></h2>
<p>The project's central metric:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">H₀ - compression_ceiling = structural_gap</span><br></div></code></pre></div></div>
<p>H₀ is byte-frequency entropy (Shannon). The compression ceiling is the best ratio standard tools achieve on a given file. The gap measures <strong>latent structure that current compressors miss</strong> — the hunting ground:</p>
<table><thead><tr><th>Data</th><th style="text-align:center">H₀</th><th style="text-align:center">Ceiling</th><th style="text-align:center">Gap</th></tr></thead><tbody><tr><td>Uniform random</td><td style="text-align:center">8.00</td><td style="text-align:center">8.00</td><td style="text-align:center"><strong>0.00</strong> — truly incompressible</td></tr><tr><td>Markov order-3</td><td style="text-align:center">8.00</td><td style="text-align:center">5.72</td><td style="text-align:center"><strong>2.28</strong> — hidden transition structure</td></tr><tr><td>Periodic 256</td><td style="text-align:center">7.82</td><td style="text-align:center">0.66</td><td style="text-align:center"><strong>7.16</strong> — massive latent pattern</td></tr><tr><td>Repeated blocks</td><td style="text-align:center">8.00</td><td style="text-align:center">1.01</td><td style="text-align:center"><strong>6.98</strong> — dictionary obvious but H₀ blind</td></tr></tbody></table>
<p>Periodic signals and repeated blocks have H₀ ≈ 8.0 — byte-frequency counters see noise. But standard compressors exploit their structure easily. The gap quantifies what's left on the table.</p>
<p>The bit-plane experiment reached 99.7% of its theoretical bound, confirming intra-byte structure is well-understood. The remaining ~38% gap to PAQ/CMIX on enwik8 requires inter-byte context — longer-range models, match models, word-level prediction. Not one missing trick, but an architecture difference.</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="philosophy">Philosophy<a href="https://bayrak.dev/blog/carnot-compression-research#philosophy" class="hash-link" aria-label="Direct link to Philosophy" title="Direct link to Philosophy" translate="no">​</a></h2>
<p>Three principles drive the project:</p>
<ol>
<li class=""><strong>Measure first.</strong> Every claim backed by the standard test corpus (10 synthetic types × 4 sizes).</li>
<li class=""><strong>Preserve the journey.</strong> Failed approaches stay in the repo — they're the map of what's been tried.</li>
<li class=""><strong>Trade time for compression.</strong> CPU-hours are abundant; novel algorithms aren't.</li>
</ol>
<p>The project achieved its goal: designing and benchmarking a novel compression pipeline that beats standard tools on structured data. The GPU and accumulator work extend the same mathematical foundation into new domains. Carnot remains open for exploration — the remaining gaps are documented, the architecture is modular, and the dead ends are mapped.</p>
<hr>
<p>Carnot is open-source under MIT. <a href="https://github.com/brsbyrk/carnot" target="_blank" rel="noopener noreferrer" class="">github.com/brsbyrk/carnot</a></p>]]></content:encoded>
            <category>rust</category>
            <category>compression</category>
            <category>gpu</category>
            <category>cryptography</category>
            <category>research</category>
        </item>
    </channel>
</rss>