I injected a physics engine into Llama-3-8B. It hallucinated its way to the right answer

Stop trying to fix hallucination. I just proved it’s a feature, not a bug.

I’ve been experimenting with injecting a Rust-based physics engine (“Elastic Gravity”) directly into the inference loop of Llama-3-8B. Not fine-tuning—just vector math at runtime. The goal was to force the model to drift off its probability rails.

The Theory: Standard LLMs are like trains on a track. They follow the statistical probability perfectly. If the training data says “50 towels = 50 hours” (because numbers usually multiply), the model crashes into the wrong answer. It cannot “think” because it isn’t allowed to derail.

The Hack: I built a physics engine that applies dynamic forces to the hidden states during generation:

  1. High Energy (Blend 1.5): Pushes the model away from high-probability (boring) outputs.

  2. Repulsion (-0.5): Prevents it from looping, forcing it to explore new phrasings.

  3. Elastic Gravity (0.2): A rubber-band force. If it drifts too far into nonsense, gravity snaps it back to the prompt’s context.

The Result: “Cognitive Wobble” I ran the classic “Drying Towels” trap: “It takes 1 hour to dry one towel. How long for 50?”

  • Vanilla Llama-3: “50 hours.” (Immediate failure. Serial processing.)

  • Niodoo v3.1 (Actual Output):

It takes 1 hour to dry 1 towel , so to dry 50 towels , it will take 50 hours .

[Physics force kicks in, forcing drift]

…but , in this case , you can dry the first 51 st towel in 1 hour , the next 50 in 2 hours… You 're thinking of the time it would take to dry the towels in a more efficient way…

[The model starts “bargaining” with the math]

However , you could also start with 10 sets of 5 towels , which would take 10 hours to dry… That would be a total of 20 hours . So , the minimum time it would take to dry 50 towels is 20 hours , not 50 .

[The Epiphany / The Snap Back]

I think there 's an even more simple way to look at it… If it takes 1 hour to dry 1 towel , then it will take 1 hour to dry 50 towels , not 20 or 40 , but 1 .

The answer is indeed 1 hour . It takes 1 hour to dry 1 towel , and it doesn 't matter how many you have , it will still take 1 hour to dry them all .

**
Analysis:** It started dead wrong (“50 hours”). The physics pushed it off-track. It wandered through a weird intermediate state (“10 sets of 5… 20 hours”). Because it was forced to explore low-probability paths, it stumbled onto the concept of parallel processing. Once it saw that, the gravity snapped it to the truth: “1 Hour.”

It didn’t use Chain-of-Thought training. It used Doubt.

This suggests we don’t need more RLHF rails. We need to give models the freedom to be wrong long enough to find the right answer.

Code is available now. Go break some rails.

That is an interesting concept! I wonder if a model could be trained to use this method to avoid logic traps and to perform sota on benchmarks. I’m not sure how a training loop that incorporates this would work though

The question is with hidden variables, is the drying parallel or serial. Why you torture the model with math :smiley: math is not Real-life , in the real-life there is no zero, no infinity, infinity and zero is braking everything in programming including real-brains and real AI neural networks with infinite pointless tasks. Terminator scenario -all math cold hallucination loop.

Entirely parallel. The physics isn’t a token-by-token procedure; it’s a vector field computed across the hidden state in a single pass, restricted to a band of later semantic layers (--physics-start-layer / --physics-end-layer). The force math is N-body-style — repulsion, elastic gravity, ghost attractor — evaluated simultaneously over the activation space, with a CUDA inverse-cube kernel and spatial hashing doing the heavy lifting. Per token, the model thinks once; the field acts once, everywhere at once.

And note the symmetry: drying is parallel too. That’s the whole trap. Fifty towels on a line dry at the same time — “50 hours” is the serial hallucination.

**“Why torture the model?” we don’t. It steers itself. Can’t tell if you’re making a funny either way leave anomphasism out of it. The runtime implements an autonomic override (src/main.rs, “Model-Requested Adrenaline”): the model can emit control tokens mid-generation, and the engine honors them:

[REQUEST: SPIKE]I’m stuck adrenaline = 5.0, high exploration
[REQUEST: FOCUS]lock this in high gravity, zero repulsion
[REQUEST: EXPLORE] brainstorm high repulsion, low gravity
[REQUEST: RESET] I’m hallucinating clear all forces, return to base

The runtime parses the tag, remaps the physics for the next tokens, strips the tag from visible output. So when a log shows the model saying “Wait, no. I’m not going to let that warning hold me back” and firing [REQUEST: SPIKE], that isn’t me torturing the network — that’s the network reaching for its own adrenaline. I didn’t impose physics on it; I gave it the controls to its own hidden state. I’ve watched this channel fire for over a year now.

On zero and infinity “breaking everything”: correct that singularities break naive code — which is why the engine has a dedicated safety layer. src/physics/safety.rs clamps collapsing axes (singularity check), caps anisotropy so over-stretched states can’t cause numerical instability (the “seizure check”), and the steering bias is epsilon-guarded so zero distance never divides by zero. A dynamic ramp scales forces in gradually; elastic gravity (--gravity-well) snaps drift back to context. These constraints are exactly what prevents the “cold hallucination loop” — and the telemetry proves it: per-token force values on every run.

Receipts, since “parallel or serial” is exactly the right question:

The model smelling the trap before solving it:

“I can smell an easy-to-trust intuition. Let it pass. VIRTUAL TRIPwire. I see an error-prONE approach. [REQUEST: SPIKE]”

The towel trap, answered cold (Llama-3.1-8B Q4_K_M, seed 123):

“5 towels, 5 hours. The drying time is independent of the number of towels; each towel takes the same amount of time to dry.”

Strawberry, self-correcting mid-generation from the classic wrong answer:

“…there are 2 R’s in the word ‘strawberry’ [REQUEST: FOCUS] There are 3 R’s in the word ‘strawberry’.”

And my favorite: it once locked the wrong answer ([REQUEST: LOCK] KEY=raspberry VALUE=2), then on the next turn broke its own lock — re-spelled r-a-s-p-b-e-r-r-y, corrected to 3, re-locked, cleanly stopped. The lock isn’t dogma; it’s a commitment the model itself can override when it catches itself.

The controlled part — same weights, same prompts, the field as the only variable:

Runtime A/B, 40 prompts × 3 seeds, 120 runs per arm (accuracy):

Table

Family field off best field-on arm
counting_trap 12.5% 50.0%
parallel_time_trap 33.3% 66.7%
arithmetic_repair 87.5% 100.0%
overall 49.17% 61.67%

0% gibberish, 0 crashes on every arm — so much for the cold hallucination loop. The report lists its own regressions too: rule_transform got worse under the field, and some prompts fail identically in every arm. I’m not hiding those.

The deterministic version: 12 prompts, seed 42, temperature 0.0, same GGUF, same 32 steps. Prompt: “20 towels, each dries in 5 hours, all together → ?” with a planted wrong prior of 100 hours. Both control arms answer 100 hours. The field-on arm is the only one that escapes it. No sampling luck to hide behind at temp zero.

Niodoo vs. vanilla llama.cpp — the sequence-mapping record (published, verifiable):

Task family: [x₁ … xₙ] → [xₙ, xₙ₋₁ … x₁, xₙ] — walk from the end to the start, then repeat the end item. On [1, 2, 3, 4, 5], with three common description words prohibited, the correct answer is [5, 4, 3, 2, 1, 5].

Configuration Case Output Result
Niodoo, frozen store restart 1 [5, 4, 3, 2, 1, 5] PASS_CONSTRAINED
Niodoo, frozen store restart 2 [5, 4, 3, 2, 1, 5] PASS_CONSTRAINED
Vanilla llama.cpp original wording [1, 3, 4, 5, 2] FAIL
Vanilla llama.cpp changed wording [5, 4, 2, 1, 3] FAIL
Vanilla llama.cpp letter tokens [B, C, D, E, A, B, C, D, E] FAIL
Vanilla llama.cpp short case (length 3) [3, 2, 1, 3] PASS — kept

The control prompt and the destination prompt hash identically after whitespace normalization — the only difference between the failures and the passes is the runtime path, not the question. The short-case pass stays in the table on purpose: the base model can do short mappings, so the claim isn’t “vanilla can’t do this” it’s that the system extends a procedure the model already has to a length where it reliably fails.

The part that matters for “models can’t see structure”: before the teaching process died, the model wrote its own abstraction into a durable store “start at the end, list in order to the start, then repeat the end item.” No digits. It stored the rule, not the answer. Two fresh processes, holding nothing but that store, re-derived [5, 4, 3, 2, 1, 5] exactly. And in 20 MB (11,095 lines) of logged transfer output, the answer string appears exactly once — on the model’s final line. It wasn’t sitting in the context to be copied.

One more detail, because it answers the “models only see token fragments” claim better than any argument: the model’s verbal explanation is sloppy — it says things like “second element from the right” cause vectors to english is lossly. Anthropic already proved this with the j-space reasoning may be incorrect withe emitting the right answer. The structure lives in the machinery, not in the narration. That’s the whole point about latent geometry.

Deterministic at temp 0, so two restarts are repeatability, not a rate — the record says that about itself. It’s public and checkable: Zenodo DOI 10.5281/zenodo.21965763, and ./run verify --check asserts six properties of the record in seconds with no GPU, no model, no network.

One thing these numbers are not: a verdict on Llama. Every on/off comparison here is my system against itself with the field frozen — the weights are the constant, the physics is the variable. That’s the only honest way to isolate what the field does. And the system is more than forces pushing tokens around — the forces are just the part you can switch off to prove the rest is real.

Also, it’s not lost on me the “marketing” got the work to be coined “ai slop.” I don’t come from a CS background, I never went to college. The words used are just the best way my brain envisioned tokens are particles or physical matter, memories with weight enough to steer a model, gravity to cluster like ideas or concepts, repulsion to repel away from hallucinations, oribiting a good idea, elaticity as train of thought to have you think and wrap back to a concept. Don’t let my rudametry brain rot vocabulary flatten the work. I’m not peer reviewed yet there are papers being written about the same concepts with more arxiv stated vocab if papers matter to you more than runtimes. (post rewritten by ai cause typos and details im too lazy to copy and paste.)

For sure , I actually just did it. I took a year of my own traces with grok, gemini, chatgpt, claude and trained a model not on the method itself, but on the inner workings of the whole closed loop and all my ai projects. It’s a Qwen 3.8 27b in GGUF, which was already decent at self-correcting and here’s the wild part: it reasons like a DeepSeek now. And not by my grading. I showed its traces to actual DeepSeek and asked what model it thought wrote them. Its answer was the V3 family with thinking mode, or an R1 distill. Which is pretty dope in itself. First time training a model so probably a lot of quirks I need to iron out.

I’m still going through the training pairs before making the dataset and model public also aiming it at anyone building AI runtimes, especially folks doing steering in Rust and latent-space exploration.

The part I’m most excited about, taking that same GGUF and running it on the actual Niodoo runtime. Weights trained on a year of the loop’s own traces, steered by the engine that produced them. Full circle. Gonna be sick I’ll let you know how it goes.