Qwenx86-4B (compact)
▶️ See it in action: Qwenx86 Debugger Space. Walk through actual recorded tool calls: the conversation, x86 registers and flags, code at the program counter, RAM and machine output, with all verified blocks on a timeline.
Qwen3.5-4B with a 64-bit x86 computer compiled into its weights: 13 transformer layers ahead of
Qwen's own. There are no learned weights in them; all of their weights are computed by a compiler.
Qwen invokes the computer through its own tool calls (<tool_call>), and the computer in the weights
executes the tool. Calculator, Python, C compiler, notes store, physics/chemistry lookup and
Python-docs lookup are all firmware in the checkpoint. This is the compact checkpoint: ~9 GB
GPU memory, so it fits on a 16 GB GPU.
v2 adds Qwenx86 memory on the same weights: a filesystem, 1,000-fact store, 50,000-token verbatim recall, JPEG images, programmable heads, a search index, exact 64-bit arithmetic and state snapshots (see Qwenx86 memory). Those run beside the model and are driven by host code; the x86 tools run entirely in the weights.
v3 lets Qwen use that memory by itself: in a chat it decides when to save, read, search, list and delete notes, and writes the tool calls on its own (see New in v3).
From Qwenputer: circuit and machinery (see Credits for details)
- The CPU circuit itself. The Qwenx86 core is a fork of Qwenputer's RV32 core (rv/core.py): fetch, ROM, RAM (delta-rule memory cells in Gated DeltaNet state), register file, branches, multiply/divide and bignum unit, designed by divagr1925.
- The compiler that converts the circuit to Qwen3.5 layers. Tool-call bridge. Chat loop. State injection. Merge layout. MicroPython build.
New for Qwenx86
- x86 in the core. Added x86 flag micro-ops (FADD, FSUB, FAND, FOR, FXOR, FINC, FDEC) and conditional jumps (JCC with the x86 condition table) to the forked circuit. Added a translator from x86-64 to the core's 32-bit ops (64-bit values as register/RAM halves, flags rebuilt per instruction, bridge ports, a single shared return dispatch).
- Memory map. 64 KB RAM (the circuit grew from 11 to 13 layers). Read-only data wired to the x86 path.
- Firmware and tools. All firmware (calc, the qcc C compiler, notes, science and python_doc stores, an x86 machine that decodes raw x86 bytes with SSE and interrupts). Code to patch the MicroPython build into a tool. Baking the firmware into the checkpoint.
- Running. Compact 16 GB loader. Block verification.
- v2 memory. Named files, facts, word-for-word recall, images, functions, a search index, exact arithmetic and saved reading state, all on the released weights.
My previous projects (neural-n64, neural-x86-doom, neural-riscv) demonstrated what it takes to exactly emulate a real CPU. This model does it in the weights of a language model.
Qwenputer vs Qwenx86
| Qwenputer | Qwenx86 | |
|---|---|---|
| CPU | RV32IM + Zba (RISC-V) core | x86-64 core: a fork of the RV32 circuit with x86 flag micro-ops and conditional jumps |
| Programs | RISC-V code | x86-64 integer code from MSVC-compiled C, translated to core ops (the python tool still runs on the core's RISC-V path) |
| Chip layers | 11 | 13 |
| RAM | 32 KB | 64 KB |
| Read-only data | 128 KB | 128 KB, also wired to the x86 path |
| x86 decoder, semantics, tests | none | from my Zen2 research; 2,000/2,000 lockstep cases vs Unicorn |
| Tools | calc and MicroPython (RISC-V builds) | calc, python (Qwenputer's MicroPython, patched), c (qcc), notes, science, python_doc, all baked into the checkpoint as boot states |
| Running | chat loop with the chip layers per token | same loop + compact 16 GB loader + block verify (up to 256 machine tokens per pass) |
| Memory | a filesystem (a separate checkpoint) | v2: files, facts, word-for-word recall, images, functions, a search index, on the same weights (qwenx86/memory/) |
| Shared base | compiler (chipformer), tool-call bridge, chat loop, state injection, merge layout | the same, reused under MIT |
What's in it
| Part | What it is |
|---|---|
| x86-64 core | 13 chip layers (64 RAM heads, 16-bit program counter). Qwenputer's RV32 circuit, forked, with x86 flag ops, conditional jumps and 64 KB RAM added. x86 decoding, semantics and tests from my Zen2 research. Runs the integer instruction set used by compiled C: 8/16/32/64-bit ALU (incl. ADC/SBB), shifts, rotates, MUL/IMUL/DIV/IDIV, MOVZX/MOVSX, SETcc/CMOVcc, BT/BTS/BTR/BTC, BSF/BSR, string ops with REP, calls and returns. Verified against Unicorn on 2,000 lockstep cases: all registers and flags after each instruction. MSVC-compiled C is translated once into the core's own ops. |
| Memory | 64 KB RAM, 256 KB code ROM, 128 KB read-only data. All in the model's Gated DeltaNet state. |
| Firmware in the checkpoint | 6 boot states (model/boot/*.safetensors). Each loads a tool's entire machine (code, data, registers, PC) from the checkpoint. No program loading on the host side. |
| x86 machine firmware | x86vm (in the dev tree, not shipped as a tool in this release). Decodes raw x86-64 bytes at run time. SSE doubles in software (bit-identical to a real SSE unit on 3,200 random ops). Interrupts (IDT, INT n, IRETQ, STI/CLI, HLT, timer). Ran hello for 1.44 M tokens through the compiled circuit, 0 wrong. |
Tools Qwen calls (all answered by the computer in the weights)
| Tool | Firmware | Notes |
|---|---|---|
calc |
exact rational calculator in C (+ - x / parens decimals) | GSM8K (first 28 test questions): 26/28 with calc vs 23/28 plain Qwen |
python |
MicroPython (Qwenputer's prebuilt RISC-V build, patched to read the tool call) | one-line expressions print their value; long programs in #more parts |
c |
qcc, a small C compiler + runner for this core | int, char, unsigned, long, pointers, arrays, functions, recursion, if/while/for, printf; long programs in //more parts |
notes_put / notes_search / notes_get |
notes store in machine RAM | no host routing; the notes are the machine's saved state |
science |
647 CODATA constants, unit conversions and the periodic table (NIST / scipy.constants), inverted index in read-only data | exact recall 647/647, retrieval@1 98.0% |
python_doc |
1,003 Python API signatures + docstrings, grammar-compressed (204 KB -> 110 KB) in read-only data | exact recall 1,003/1,003 |
Qwenx86 memory (new in v2)
v2 gives the model a memory it can write to and read back exactly: named files, a store of facts,
long documents recalled word for word, images, small functions, a search index, exact arithmetic,
and saved reading state. It uses the same weights as v1 (nothing retrained or re-merged); the
code is in qwenx86/memory/. Every result below was measured on these weights, from this folder.
How to use it: v2/README.md.
How it works, and what it is not:
- Where the memory lives. Each of four of the model's layers (13-16) gets extra memory slots of the same kind the layer already uses (DeltaNet state). Ordinary text can't write to them or wear them down, so Qwen's own answers are exactly the same whether the memory is full or empty.
- What is not in the weights. The extra slots are run by code alongside the model, not stored as new weights. Finding a file by name (hashing it, comparing names, stepping through the directory) is also ordinary Python. So unlike the x86 tools above, which run entirely inside the model, the memory is driven from outside it, and in v2 you use it through Python calls (v3 adds a chat where Qwen calls it itself).
- How text comes back out. Stored text is printed through the model's own final layer: each stored token is turned into a signal the output layer turns back into that same token.
| Feature | What it does | Result |
|---|---|---|
| Named files | save, read, add to, delete and list files by name | 51 files saved and all read back correctly after restarting; 400 random operations, 0 mistakes; Qwen's answers unchanged afterwards |
| Facts | 1,000 short facts stored and looked up by subject | all 1,000 correct after restarting; unknown subjects answer "not found" |
| Word-for-word recall | a 50,000-token document stored as one file | read back with 0 errors in 50,000 tokens; 100 random passages printed by the model, 0 errors |
| Printing stored text | the model writes stored text out itself | a 104-token passage printed exactly in a single step, 13x faster than token by token |
| Images | 20 JPEG pictures stored byte by byte | all 20 come back identical; Qwen's vision encoder sees them exactly as it saw the originals |
| Functions | small lookup functions (squares, primes, bit tricks, a 128x128 matrix) written into blank memory slots at run time | 10,000 inputs, 0 wrong; the matrix within 0.2 % |
| Search index | find the stored passage that answers a question | 100 % correct for a word-matching index (16-128 passages); 90 % for pictures; a meaning-based text index is weak (59 % at best) |
| Exact arithmetic | add, subtract, AND and XOR on 64-bit numbers using compiled lookup tables | 1,000 / 1,000 correct; plain Qwen3.5-4B got 979 of the same 1,000 wrong |
| Saved reading state | read a document once, save the model's state, answer questions later without the document | 4 / 4 questions answered correctly from the saved state, matching a full re-read |
Notes: the arithmetic tables come from the author's Zen2 work and ship as model/atoms.safetensors
(60 KB). The image features use Qwen3.5-4B's own vision encoder, loaded separately, because this
checkpoint is text-only. Background reading: docs/state_writes.md (why writing to this kind of
memory doesn't need a full model run) and docs/d1_trace.html (every table lookup behind the
arithmetic results, checked by an independent program, 0 mismatches).
New in v3: Qwen uses its memory itself
In v2 you drive the memory from Python. In v3, Qwen does it: in a chat it gets five memory tools (save, read, search, list, delete), decides when to use them, and writes the tool calls itself. Python carries out each call and hands the result back, the same way the v1 tools answer. The memory is saved after every message, so it is still there in the next chat. Same weights as v1 and v2; nothing retrained.
A real chat on the released model:
you> Remember that my locker number is 412. Save it in your memory.
[memory_save {"name": "locker_number", "text": "412"} -> saved]
qwen> Okay, I've saved your locker number as 412.
you> What is my locker number? Check your memory.
[memory_read {"name": "locker_number"} -> 412]
qwen> Your locker number is 412.
| Test | Result |
|---|---|
| Session 1: save three notes (wifi, dentist, bike), then delete wifi | Qwen called memory_save three times and memory_delete once, all succeeded |
| Session 2, a separate process that only gets the saved memory | Qwen found the dentist appointment and the bike code with its own searches, listed "dentist, bike", and said it had no wifi note: 4 / 4 |
Every tool in one chat. python -m qwenx86.mixed offers all eleven tools at once: the six v1
tools, run by the computer in the weights, and the five memory tools, run by Python. Qwen picks;
the chat sends each call to the right place. A v1 call still runs inside the model with every
machine token checked against the reference, exactly as in v1.
| Session | You say | Tool Qwen called | Run by | Result |
|---|---|---|---|---|
| 1 | "What is 1234 * 5678? Use the calc tool." | calc |
the computer in the weights, 43,770 machine tokens | 7006652 |
| 1 | "Save a file called locker with the memory_save tool: my locker number is 412." | memory_save |
the memory (Python) | saved |
| 1 | "Write a C program that prints 6 * 7 and run it with the c tool." | c |
the computer in the weights, 169,293 machine tokens | 42 |
| 1 | "Save a note called bike with the notes_put tool: the bike lock code is 7193." | notes_put |
the computer in the weights, 117,746 machine tokens | saved |
| 1 | "What is my locker number? Use the memory_search tool." | memory_search |
the memory (Python) | 412 |
| 1 | "What is my bike lock code? Use the notes_search tool." | notes_search |
the computer in the weights, 125,199 machine tokens | 7193 |
| 2 | "What is my locker number? Use the memory_search tool." | memory_search |
the memory (Python) | 412 |
| 2 | "What is my bike lock code? Use the notes_search tool." | notes_search |
the computer in the weights, 125,197 machine tokens | 7193 |
All 8 right, each call went to the tool Qwen named, and 0 of 581,205 machine tokens differed from the reference.
How to use it: v3/README.md.
The tests
Is the model still Qwen?
- Text match vs stock Qwen3.5-4B. 3 normal text prompts (48 token positions) run through both stock Qwen and Qwenx86. The most likely next token is the same at each position (48/48). The distributions have a KL divergence of 4.1e-4; the layer widening alone is ~5e-4. So the computer layers don't change Qwen's own behavior.
- Text logits with memory loaded. Qwen's text logits differ by exactly 0.0 between notes/store state loaded and empty. No leakage of computer state into Qwen's text.
Is the CPU a proper x86-64 CPU?
- Instruction coverage (2,000 cases). 4 generators from my Zen2 research: general instructions, 2-byte opcodes, multiply/divide, string. 500 cases each. Each case runs on our core and on Unicorn (a real x86-64 emulator) in lockstep. All 16 registers and all flags x86 defines must be the same after each instruction. 2,000 / 2,000.
- Compiled C programs. MSVC-compiled C programs (hello, a small library with string/sort routines, a program with recursion/multiply/divide/hex print) run on our core and on Unicorn. Printed output and registers after each instruction are the same.
- Through the compiled circuit. The same programs run through the actual transformer layers (not just the reference), token by token: hello 386, lib 16,370, prog 358,501 tokens, 0 wrong each.
- 64 KB RAM. A program that fills and reads back the upper 32 KB (new in this model) prints the same checksum as on Unicorn.
Do the firmwares compute the correct answers?
- calc. 125 expressions (random arithmetic + GSM8K-like steps with decimals). Exact fraction arithmetic. Same firmware on Unicorn. 0 wrong.
- python. 9 programs through the bridge: 2**100, sums, list comprehensions, recursion, an error
traceback, strings, a 3-part
#moreprogram. Correct output in chat framing. - c (qcc). 10 programs: sum of squares, recursive Fibonacci, prime counting, strings, pointers,
unsigned long long, divide by zero, a syntax error, a too-long call, a 3-part
//moreprogram. Each output is as expected AND the same as the same firmware on Unicorn. - notes. 10 operations: save, replace, case-insensitive get, search, no match, not found.
- science. All 647 records looked up by name: 647/647 exact. 849 natural queries: correct record first 98.0% of the time (the word-overlap ceiling). The firmware's answer is the same as a Python copy of its algorithm for all 1,496 lookups.
- python_doc. All 1,003 records looked up by name, decompressed byte-exact: 1,003/1,003. An unknown name answers "not found".
Does it work as a chat on real weights?
- Live chats on a 16 GB card (compact checkpoint, computer booted from the baked firmware, block verify): Qwen wrote the tool call, the machine executed it, and Qwen replied based on the result. 0 lock errors in all cases. These are the runs shown in the Space; times include recording the debugger frames.
| Chat | What happened | Machine tokens | Time |
|---|---|---|---|
| calc | (1200 - 350) / 5 = 170 | 56,667 | 41.0 s |
| python | 2**100 = 1267650600228229401496703205376 | 39,526 | 46.8 s |
| c | Qwen wrote a C program that prints 6 * 7. Compiled and executed: 42 | 169,293 | 71.2 s |
| notes | 1) "save a note called bike..." saved. 2) Same chat: "what's the code?" -> 7193 | 117,746 + 74,798 | 81.6 s |
| science | Avogadro constant: 6.02214076e+23 mol^-1 | 402,834 | 128.9 s |
| python_doc | math.gcd signature: math.gcd(*integers) | 55,684 | 44.3 s |
- Notes across processes. Save notes in one process, write the machine state to disk, boot a new process from it. All 3 notes found.
- GSM8K with calc (first 28 test questions, greedy). 26/28 with the calc tool, 23/28 with just Qwen. 31 calc calls, 2.2 M machine tokens, 0 lock errors.
- Speed. A C chat (168,578 machine tokens): 582 s token by token, 44.5 s with block verify (13x).
Screenshots
All runs from the Space, at the end of the run.
Run it
Needs a CUDA GPU with 16 GB of memory (the model uses ~9 GB, plus room for the run), Python 3.12 and
the packages from requirements.txt (PyTorch with CUDA, transformers==5.22.1).
pip install -r requirements.txt
python -m qwenx86.run --tool calc "What is (1200 - 350) / 5? Use the calc tool."
python -m qwenx86.run --tool c "Write a C program that prints 2 to the power 40. Run it with the c tool."
python -m qwenx86.run --tool python "What is 2 to the power 100 exactly? Use the python tool."
python -m qwenx86.run --tool science "What is the Avogadro constant? Use the science tool."
python -m qwenx86.run --tool pydoc "What is the signature of math.gcd? Use the python_doc tool."
python -m qwenx86.run --tool notes "Save a note called bike. The bike lock code is 7193."
python -m qwenx86.run --tool notes "What's the code for my bike lock? Check your notes."
Run from this folder. The model loads in ~25 s. The transcript goes to stdout; the summary (machine
tokens, verified blocks, lock status) goes to stderr. The notes tool stores its memory in
./qwenx86_notes/.
The v2 memory checks (results and saved state go to ./qwenx86_memory/):
python -m qwenx86.memory files save
python -m qwenx86.memory files load
python -m qwenx86.memory facts save
python -m qwenx86.memory facts load
python -m qwenx86.memory verbatim
python -m qwenx86.memory arithmetic
python -m qwenx86.memory images save
python -m qwenx86.memory images load
python -m qwenx86.memory lists them all; how to use the memory is in v2/README.md.
v3, a chat where Qwen uses the memory itself, and one chat with every tool (v3/README.md):
python -m qwenx86.memory chat
python -m qwenx86.mixed
Files
| Path | Contents |
|---|---|
config.json |
a copy of model/config.json at the top of the repo, where the Hub counts downloads |
model/compact.safetensors |
compact weights (8.75 GB): all maps' nonzero rows x columns |
model/boot/*.safetensors |
baked machine states for the 6 tools |
model/*.json, tokenizer |
config, chipformer.json (merge layout), Qwen tokenizer |
machines/*.pkl, tools.json |
reference parked machine for each tool (checks all tokens), tool schema |
qwenx86/ |
runtime: loader, x86 core circuit, uop machine, block verify, CLI |
qwenx86/memory/ |
v2: the memory and its checks (python -m qwenx86.memory) |
model/atoms.safetensors |
v2: the arithmetic lookup tables, 60 KB |
docs/ |
v2: state_writes.md, d1_trace.html |
v2/README.md |
v2: how to use the memory |
v3/README.md |
v3: the memory chat |
chipformer/ |
Qwenputer's compiler and RISC-V core and tools (MIT, Divyansh Agrawal) |
Limits
- Code per tool call: ~60 tokens per part (the bridge captures 80 tokens of a tool call). Longer
programs go in
//more/#moreparts (up to 3,000 chars). - The x86 translator has no indirect jumps and no hardware SSE (x86vm does SSE in software, but is not shipped as a tool). The python tool is Qwenputer's minimal MicroPython build (e.g. no slice steps, a small heap).
- Long runs: the longest local chat was 814,561 machine tokens (science, "atomic mass of iron", 202 s); machine tokens skip Qwen's layers, so they don't fill Qwen's KV cache.
- Memory (v2): it runs alongside the model and is driven by Python code, not by the model itself. File names are at most 6 tokens.
- Memory chat (v3):
qwenx86.memory chatoffers only the memory tools;qwenx86.mixedoffers every tool. With both note systems available, name the tool you want, or Qwen picks one. - Speed: the machine makes 1 token per step. Block verify batches the checks, but a C compile + run is 0.1–6 M machine tokens.
- x86 decoding: programs are translated to core ops before running. Raw x86 bytes are decoded by firmware, not by circuit layers. Qwenputer also stores RISC-V code pre-decoded.
- Qwen's text: Qwen3.5-4B's text, unmodified, and not trained on these tools. It sometimes restyles a tool's value in its answer (e.g. 6.02214076e+23 -> 6.02214076 × 10²³).
Credits
- Qwenputer by Divyansh Agrawal (@divagr1925,
github.com/divagr18/qwenputer, MIT) is the base of this
model. From Qwenputer:
- chipformer: the compiler that converts the circuit to Qwen3.5 layers (q35 IR, emitter, primitives). Included here unmodified, MIT license (chipformer/LICENSE).
- The RV32 core (chipformer/rv/core.py), with delta-rule memory cells, ROM, DROM and the bignum unit. Qwenx86's x86 core is a fork of it (+ x86 flag uops, conditional jumps, 64 KB RAM).
- The tool-call bridge (rv/bridge.py): captures
<tool_call>, runs the machine, echoes the output to the chat. The reference Session for checking all tokens. - The chat loop (rv/chat.py, RvChat). HF emission/state injection (rv/hf.py). The merge layout (rv1). The circuit executor. The nonzero block compaction idea (q35/fast.py).
- The MicroPython build (mpy-s1.elf, from Qwenputer's tests). We patch it word by word into the python tool.
- v2 memory: the design of the file store follows Qwenputer's filesystem (divagr1925/Qwen3.5-4B-Qwenputer-Filesystem): how tokens are packed into memory slots, how names are found in the directory, the size limits, and keeping Qwen's own layers untouched while the memory is used. The code that runs it on Qwenx86, the printing and the tests are our own.
- From my previous work: the x86-64 reference and atoms from my Zen2 research, and lessons from neural-n64, neural-x86-doom and neural-riscv.
- Model weights: Qwen/Qwen3.5-4B (Apache-2.0). Qwen's own weights are unmodified. Data: NIST CODATA / scipy.constants (public domain), periodictable (public domain), CPython docstrings (PSF license). Unicorn was used as the x86 reference for tests.
Citation
If you use this model, please cite this model and Qwenputer, which it is based on:
@misc{quazim0t02026qwenx86,
author = {Quazim0t0},
title = {{Qwenx86}: 64-bit x86 computer compiled into weights of {Qwen3.5-4B}},
year = {2026},
publisher = {Hugging Face},
howpublished = {\url{https://huggingface.co/Quazim0t0/Qwenx86-4B-compact}},
note = {x86 side from author's Zen2 research. CPU circuit forked from Qwenputer}
}
@software{agrawal2026qwenputer,
author = {Agrawal, Divyansh},
title = {{Qwenputer}: a computer compiled into the weights of a pretrained language model},
year = {2026},
version = {0.1.0},
url = {https://github.com/divagr18/qwenputer},
license = {MIT}
}
License
The weights are Apache-2.0 (a modified version of Qwen/Qwen3.5-4B, LICENSE-Qwen3.5). We widen the
config and add the computer layers, but don't modify Qwen's own weights.
chipformer/ is MIT, © 2026 Divyansh Agrawal (chipformer/LICENSE).