Correctness

mojo-dllm is checked at four levels, each against an implementation it does not share code with.

1. Formats and kernels (unit tests, every commit)

  • GGUF parsing, Q4_K / Q5_K / Q6_K / Q8_0 / F16 / BF16 decoding: compared with the gguf Python package's dequantization on fixtures it generated (max abs difference 1e-6).
  • Both GEMMs: compared with an f64 reference over the same dequantized weights; relative error under 1% with int8 activations.
  • RMSNorm, RoPE, softmax, bidirectional attention, SwiGLU: closed-form cases.
  • The forward pass of a 2-layer model with random K-quant weights: compared with a numpy forward pass; relative logit error under 2% on the LLaDA model and under 5% on the Dream model (its large Q/K biases sharpen the softmax, which amplifies int8 error), and the same argmax on every position.

The GPU has its own suite, tests/gpu/test_gpu.mojo, which runs on a machine with an NVIDIA GPU (scripts/run-gpu-tests.sh; CI only builds it). It checks each GPU kernel against the CPU function it replaces, the int8 mma fragment layout against a CPU matmul, and the GPU forward pass of both tiny models against the numpy reference with a 2% bound on both models.

2. Tokenizer (unit tests, every commit)

36 strings covering English, contractions, numbers, accents, combining marks, CJK, Cyrillic, Arabic, Devanagari, emoji, CRLF, non-breaking and em spaces, code, and LLaDA's chat-template special tokens. Every id matches llama-tokenize on the real LLaDA GGUF, and decoding restores the input byte for byte.

3. Logits on the real model

tools/ref/llada_ref.cpp links libllama and dumps llama.cpp's logits for a fixed token sequence; mojo-dllm logits dumps ours; scripts/compare_logits.py compares them. The sections marked GPU run mojo-dllm logits --device gpu.

The GPU sections also compare against llama.cpp built with CUDA. That build quantizes activations with one scale per 32 values, as the GPU path does, while llama.cpp on the CPU uses one per 256, as the CPU path does. On Dream's prompt rows the two llama.cpp builds disagree with each other, so a single reference would mix that spread into mojo-dllm's numbers. The table under each GPU section shows all three pairs.

LLaDA-8B-Instruct.Q4_K_M.gguf

Canvasrowsmedian cosinemasked rows medianworst cosinemedian rel. RMSsame argmaxtop-5 overlap
64 tokens640.999850.999880.9951 (row 13)0.01862 / 644.83 / 5
126 tokens1260.999880.999890.9992 (row 19)0.017120 / 1264.67 / 5

Evidence: bench/parity/2026-10-03-llada.json (mojo-dllm 0b3e17a).

LLaDA-8B-Instruct.Q4_K_M.gguf, GPU

Canvasrowsmedian cosinemasked rows medianworst cosinemedian rel. RMSsame argmaxtop-5 overlap
64 tokens640.999810.999830.9960 (row 13)0.02162 / 644.77 / 5
126 tokens1260.999880.999880.9984 (row 24)0.017120 / 1264.74 / 5

The same canvases against a second reference, llama.cpp CUDA, and the two references against each other:

Canvascomparedmedian cosineworst cosinemedian rel. RMSsame argmax
64 tokensmojo-dllm vs llama.cpp CPU0.999810.99600.02162 / 64
64 tokensmojo-dllm vs llama.cpp CUDA0.999880.99520.01760 / 64
64 tokensllama.cpp CPU vs llama.cpp CUDA0.999720.99600.02660 / 64
126 tokensmojo-dllm vs llama.cpp CPU0.999880.99840.017120 / 126
126 tokensmojo-dllm vs llama.cpp CUDA0.999920.99870.015120 / 126
126 tokensllama.cpp CPU vs llama.cpp CUDA0.999830.99730.021117 / 126

Evidence: bench/parity/2026-10-09-llada-gpu.json (mojo-dllm cedc0b5).

Dream-v0-Instruct-7B-Q4_K_M.gguf

Canvasrowsmedian cosinemasked rows medianworst cosinemedian rel. RMSsame argmaxtop-5 overlap
71 tokens710.999930.999940.9668 (row 26)0.03264 / 714.61 / 5
133 tokens1330.999860.999880.9782 (row 11)0.03392 / 1334.22 / 5

Evidence: bench/parity/2026-10-03-dream.json (mojo-dllm 0b3e17a).

Dream-v0-Instruct-7B-Q4_K_M.gguf, GPU

Canvasrowsmedian cosinemasked rows medianworst cosinemedian rel. RMSsame argmaxtop-5 overlap
71 tokens710.999910.999930.9455 (row 26)0.04262 / 714.63 / 5
133 tokens1330.999840.999860.9016 (row 11)0.03785 / 1334.22 / 5

The same canvases against a second reference, llama.cpp CUDA, and the two references against each other:

Canvascomparedmedian cosineworst cosinemedian rel. RMSsame argmax
71 tokensmojo-dllm vs llama.cpp CPU0.999910.94550.04262 / 71
71 tokensmojo-dllm vs llama.cpp CUDA0.999940.81790.02064 / 71
71 tokensllama.cpp CPU vs llama.cpp CUDA0.999870.64920.04462 / 71
133 tokensmojo-dllm vs llama.cpp CPU0.999840.90160.03785 / 133
133 tokensmojo-dllm vs llama.cpp CUDA0.999900.89750.02297 / 133
133 tokensllama.cpp CPU vs llama.cpp CUDA0.999790.87840.03980 / 133

Evidence: bench/parity/2026-10-09-dream-gpu.json (mojo-dllm cedc0b5).

Exact equality is not the goal: both runtimes quantize activations to int8, but they block and accumulate in different orders. The llama.cpp maintainers recommend the same kind of statistical comparison for diffusion models.

The table scores every position of each canvas. On Dream the median agrees as closely as on LLaDA, but a few prompt positions do not: they sit on special tokens and newlines, where Qwen-family models carry very large outlier activations that magnify int8 rounding. Masked positions, the ones the sampler reads, agree closely. Which of the two runtimes is nearer an f32 reference at those outlier rows is not measured yet.

4. Generated tokens on the real model

llama.cpp's own diffusion example cannot serve as a token-level reference. On the pinned commit it never applies --diffusion-cfg-scale or --diffusion-alg-temp, adds Gumbel noise to one row only, ranks entropy in the wrong direction, and samples from the distribution even at temperature 0. So llada_ref generate re-implements LLaDA's generate.py (temperature 0, low-confidence remasking) on top of llama.cpp's logits, and the result is compared token by token with mojo-dllm run. For Dream, llada_ref generate-dream does the same with Dream's diffusion_generate.

A diffusion loop amplifies small differences: when two candidates are nearly tied, the runtimes can commit different tokens at one step, and every later step then sees a different canvas. A run that matches only up to some position therefore says where the first near-tie fell, not that the algorithm differs. A run that matches every token, as the prime-checking prompt does on LLaDA, shows that the loops agree.

LLaDA-8B-Instruct.Q4_K_M.gguf

Promptgeneratedstepssame tokensfirst difference
Explain speculative decoding in two sentences.32328 / 32position 5
Write a short Python function that checks whether a number is prime.643264 / 64none

Evidence: bench/parity/2026-10-03-llada.json (mojo-dllm 0b3e17a).

LLaDA-8B-Instruct.Q4_K_M.gguf, GPU

Promptgeneratedstepssame tokensfirst difference
Explain speculative decoding in two sentences.32328 / 32position 5
Write a short Python function that checks whether a number is prime.643264 / 64none

Evidence: bench/parity/2026-10-09-llada-gpu.json (mojo-dllm cedc0b5).

Dream-v0-Instruct-7B-Q4_K_M.gguf

Promptgeneratedstepssame tokensfirst difference
Explain speculative decoding in two sentences.323224 / 32position 22
Write a short Python function that checks whether a number is prime.643243 / 64position 3

Evidence: bench/parity/2026-10-03-dream.json (mojo-dllm 0b3e17a).

Dream-v0-Instruct-7B-Q4_K_M.gguf, GPU

Promptgeneratedstepssame tokensfirst difference
Explain speculative decoding in two sentences.323226 / 32position 23
Write a short Python function that checks whether a number is prime.643228 / 64position 1

Evidence: bench/parity/2026-10-09-dream-gpu.json (mojo-dllm cedc0b5).

Reproduce

c++ -O2 -std=c++17 tools/ref/llada_ref.cpp -I$LLAMA/include -I$LLAMA/ggml/include \
    -L$LLAMA/build/bin -lllama -lggml -lggml-base -Wl,-rpath,$LLAMA/build/bin -o build/llada_ref
uv run --only-group fixtures python bench/parity.py --arch llada --model models/LLaDA-8B-Instruct.Q4_K_M.gguf
uv run --only-group fixtures python bench/parity.py --arch dream --model models/Dream-v0-Instruct-7B-Q4_K_M.gguf
uv run --no-project python scripts/bench_table.py --write