EschaMeetsHiggs

Escha-W2 / Apple M4 / updated August 4-5, 2026

Escha-W2 runs on Metal.Feel the difference.

EschaLabs' Escha-W2 is a 2-bit quantized build of Qwen3.6-35B-A3B that shipped CUDA-only. Higgs runs it on Apple Silicon — format decoded from the tensors, codebook in a Metal kernel, experts left in trellis form instead of expanded. A 35B MoE packed in 11GB that serves from a 4 year old laptop at 29 tok/s.

01 / What is actually new

First MLX implementation

We know of no other implementation that reads the Escha-W2 tensor format on MLX. Not a claim to have invented EXL3 — exllamav3 is the reference and the source of the method. A claim about portability: the published build is CUDA sm_80–120, this one runs the same weights on Metal. If we missed one, open an issue and we will correct it.

The format's names return nothing

GitHub code search: escha_code, zero results. escha_rout, zero results. eschamoe returns OCR noise in scanned Latin texts, plus one substantive hit.

An independent assessment, one day earlier

That hit is pestopoppa/epyc-root, dated 2026-08-03: “The eschamoe format itself is unusable here. It is closed CUDA sm_80-120 with no ROCm path.” It logs a third-party codebook probe as blocked, “2–4 weeks.”

An official runtime is coming

EschaLabs are releasing their own MLX runtime within days. This is a timestamp, not a flag in the ground — and the durable parts of this page are the format spec and the norm-offset trap, which apply to any implementation, theirs included.

02 / The contribution

The format, written down

Derived from the tensors, verified against the unquantized base model, and not documented publicly anywhere else.

escha_code   I16  [E, in/16, out/16, 16*K]   packed trellis codes
escha_rin    F16  [E, in]                    per-input-channel scale
escha_rout   F16  [E, out]                   per-output-channel scale
escha_s_in   F32  [E, in]                    all 1.0 in this release
escha_s_out  F32  [E, out]                   all 1.0 in this release
escha_config I32  [9] = [tile=16, K, bits, mcg_flag, num_experts, in, out, in_p, out_p]

Reconstruction puts the scales outside the Hadamard, which is what makes the kernel cheap — H is symmetric orthonormal, so the transform lands on activations and never on weights:

W[out, in] = (H128 · Ŵ · H128 * rin[:,None] * rout[None,:]).T

su = rin*s_in ; sv = rout*s_out
xh    = had_blockwise(x * su, -1)
y_pre = xh @ Ŵ                  # trellis matvec, no Hadamard, no scales
y     = had_blockwise(y_pre, -1) * sv

Codebook 1 decode, three instructions:

x = code * 0xCBAC1FED
x = (x & 0x8FFF8FFF) ^ 0x3B603B60
v = f16(x.lo) + f16(x.hi)

A 16×16 tile is 256 codes packed into 16*K int16 words. Code i is the 16-bit window at bit offset i*K, circular — tail-biting, so adjacent codes share 16-K bits. Tile elements sit in a tensor-core permutation, not row-major. The closed-form inverse, for output column c and lane pair (q, rh):

t = 4*(4*(c&7) + q) + 2*((c>>3)&1) + rh
r = 8*rh + 2*q + {0,1}
e = r & 1

Two dead ends, so you skip them

|rin| is not a scaled sign vector — it holds hundreds of distinct magnitudes, so the Hadamard block size cannot be recovered from it. And the codebook constants cannot be MLX template arguments: MLX bakes template ints into the MSL function identifier, and a high-bit u32 prints negative, producing invalid MSL. Pass them as a kernel input.

03 / Is the decode right

It sits on the rate–distortion bound

Cosine similarity against the unquantized base is a weak test — it tells you the numbers are close, not that the decode is correct. The stronger test is the Shannon bound. For a Gaussian source at rate R, relative Frobenius error cannot beat 2^-R. The Hadamard transform Gaussianizes the weights, and trellis coding then approaches that limit. A decode with a subtle systematic error cannot land within a few percent of the Shannon limit at precisely the rate the container stores.

Measured vs the theoretical floor

rel. Frobenius error — lower is better
TensorRateR(D) boundMeasured
L0 gate_up2.00 bpw0.25000.2422
L39 gate_up2.00 bpw0.25000.2920
L20 e7 gate_up2.00 bpw0.25000.2917
L0 down_proj3.00 bpw0.12500.0824
L39 down_proj3.00 bpw0.12500.1390

Note the rate is not uniform despite the W2 name: gate_up_proj is K=2, down_proj is K=3. That alone explains the 0.945–0.990 cosine “band” an earlier pass saw as one population — it is two, each sitting at its own bound.

Against naive quantizers, same tensor

cosine vs unquantized base — L0 gate_up, expert 0
escha 2-bit
0.9702
RTN 2-bit g64
0.9062
RTN 2-bit ch
0.8368
RTN 4-bit g64
0.9956

The 4-bit row is a calibration reference, not a peer — it costs twice the bits. Against every genuine 2-bit competitor our decode wins by a wide margin, and lands three-quarters of the way to 4-bit while storing 2 bits.

The error is unbiased

cosine == sqrt(1 - rel²) holds to four decimals — 0.9702/0.9702, 0.9903/0.9903. That identity is true only when the residual is orthogonal to the reconstruction, the MMSE condition. Naive RTN absmax fails it (0.8368 vs 0.7979) because its level grid is biased. A wrong permutation, wrong Hadamard, or misordered scale would inject signal-correlated error and break it.

The residual is white

Per-channel and mod-16 / mod-128 periodicity were tested against a 200-draw permutation null. Not one observed ratio is significant at p<0.05, on either axis, either stride. Most sit below the null median. No structure means no geometry bug hiding under the average.

The kernel is exact

Metal tile decode vs the CPU reference: bit-identical, max diff 0, for K ∈ {2,3,4}. The gather matvec agrees with CPU at 2.1e-7 relative on synthetic input and 3.7e-7 on real weights.

04 / What it costs to run

Results

Keeping the experts in trellis form is not only smaller — it is the difference between fitting and not fitting. On a 32 GB machine the expanded path drives the system into swap; the native path leaves room for KV cache.

Resident weights

GB, MLX active-memory counter — lower is better
native trellis
11.16
expanded affine
21.66

Time to first load

seconds, cold — lower is better
native trellis
6.2
expanded affine
140.9

22.7× faster to start, because nothing is decoded on the CPU at load. The trellis codes go to the GPU as they are.

Throughput by context

Qwen3.6-35B-A3B-Escha-W2, 128 generated tokens, cold prefill
ContextPrompt tokTTFTPrefill tok/sDecode tok/s
1k10245.34 s190.629.3
4k409621.68 s188.427.5
8k819246.27 s177.426.1

Mean of four runs with low-power mode off — two on battery, two on wall power. Prefill holds within 7% from 1k to 8k and decode within 11%: this model is hybrid attention, so only 10 of its 40 layers grow KV with context.

The full power spectrum

decode tok/s — same build, same prompts
Condition1k4k8k
low-power mode ON, battery22.521.115.1
LPM off, battery (n=2)28.926.926.0
LPM off, wall power (n=2)29.728.126.3
AC vs battery+2.6%+4.8%+1.0%

Wall power buys nothing measurable. With low-power mode off, AC and battery differ by −1.2% to +4.8% — inside run-to-run noise. Low-power mode is the whole story, worth +34–40% prefill.

The top row also carries a fix, not just a power setting: the old harness warmed with 256 tokens, which routes to a handful of 256 experts, so later runs were still paging weights in. That alone was worth 73% at 8k. Warm an MoE wide.

05 / The part that took the longest

One subtraction, forty layers

The kernel was bit-exact and the decode sat on the Shannon bound, and the model still emitted pure token salad. The cause was not in the trellis at all: escha stores RMSNorm weights centred on zero, as w - 1. Passing them through untouched left every norm scaling by ≈0 instead of ≈1.

tensor                        escha    base   |e-b|  |e+1-b|
layers.0.input_layernorm      0.031   1.031   1.004    0.004
layers.3.self_attn.q_norm     0.325   1.325   1.004    0.004
final model.norm              1.628   2.628   1.008    0.008
layers.0.linear_attn.norm     0.884   0.884   0.000    1.000   <-- exception

101 tensors carry the offset; the 30 linear_attn.norm tensors — the gated norm inside the GDN block — do not. The residual after adding 1 back is pure bf16 rounding. Effect on the first token of "1 2 3 4 5 6 7 8":

beforeafter
top-1 tokengetti9
top-1 logprob−7.379−0.0047
top-1 probability0.06%99.5%
top-5 spread0.12 nats (flat)7.0 nats

Uniform over the 248,320-token vocabulary would be −12.42, so the broken model was barely above noise. The lesson is cheap to state and was expensive to learn: coherent generated text should be the first gate, not the last. Tensor cosine and bit-identical kernels both passed while the model was unusable.

06 / Run it yourself

Reproduce

Apple M4, 10 GPU cores, 32 GB unified memory, macOS 27.0, Xcode 26.0.1, higgs 1.6.0. The work lives on the nightly branch — Homebrew and crates.io track upstream, so build from source. The native trellis path is the default; no flag is needed.

git clone -b nightly https://github.com/dusterbloom/higgs
cd higgs && cargo build --release -p higgs

# add the checkpoint to ~/.config/higgs/config.toml, then:
./target/release/higgs serve

curl -s http://127.0.0.1:9000/v1/completions \
  -H 'Content-Type: application/json' -H 'Authorization: Bearer higgs' \
  -d '{"model":"escha-35b-a3b","prompt":"1 2 3 4 5 6 7 8",
       "max_tokens":8,"temperature":0,"logprobs":true,"top_logprobs":3}'

HIGGS_ESCHA_NATIVE=0 selects the expanded affine path for comparison. higgs doctor reports the resident estimate for whichever mode is active, reading the per-projection trellis rate from quantization_config.layer_meta. The fidelity numbers come from tools/escha_forensics.py; the throughput table from a TTFT/inter-token harness over /v1/completions with unique random prompts so the prefix cache cannot serve them.

Two traps if you benchmark an MoE this way. Warm up with enough tokens to route to most experts — 256 tokens touches a handful of 256, and every later run is then still paging weights in. And reject runs that hit EOS early: a 13-token generation measures decode over twelve intervals and is not comparable to a 128-token one. Both corrupted our first published numbers.

07 / Scope

What this measures

Weight fidelity and inference cost

No task benchmarks — no HumanEval, no MBPP, no agentic eval. For model quality see EschaLabs' own published results.

Four runs, one machine

One checkpoint, one batch size, an M4 with 32 GB. Sustained multi-hour load was not tested.

Memory and load time, not speed, for the affine path

At 21.7 GB resident on a 32 GB machine it swaps, so its throughput would measure the swap, not the kernel.

Fidelity sampled, not exhaustive

Layers {0,1,3,20,39} × experts {0,7}, consistent in every cell measured.