Skip to content

M10.5: Bumblebee quantized inference integration - #20

Merged
ausimian merged 1 commit into
mainfrom
m10.5-bumblebee-quantized-inference
Apr 15, 2026
Merged

M10.5: Bumblebee quantized inference integration#20
ausimian merged 1 commit into
mainfrom
m10.5-bumblebee-quantized-inference

Conversation

@ausimian

Copy link
Copy Markdown
Owner

Summary

Closes the gap M10 left open: Native.quantized_matmul is now reachable from Nx.Defn.jit-traced Axon forward passes, and a quantized Qwen3-0.6B greedy-decodes end-to-end through stock Bumblebee.Text.generation/4.

  • Emily.Quantization.dequantize_defn/1 — defn-native MLX affine dequantize built from Nx.right_shift / bitwise_and / multiply / add. Supports bits ∈ {2, 4, 8}.
  • Emily.Quantization.Layers.quantized_dense/4 — Axon-compatible layer op. Nx.dot(x, dequantize_defn(qw)) replaces MLX's fused quantized_matmul (two kernels vs. one; M11 closes the gap).
  • Emily.Quantization.Transform.quantize/3 — graph rewriter + model-state quantizer mirroring Axon.Quantization.quantize/2. Rewrites every :dense node to :quantized_dense and swaps kernels for %QuantizedWeight{}.
  • :qwen3_quant_full opt-in conformance — loads dense Qwen3-0.6B, quantizes via Transform.quantize/3 (bits=4, group_size=128, transpose=true), greedy-decodes 32 tokens, pins continuation text as regression gate.

Approach chosen: defn-native dequantize (Option 1 from PLAN.md M10.5). Rejected Option 2 (fork Nx.Defn.Evaluator — fragile) and Option 3 (upstream Nx custom-op hook — too slow).

Scope reductions from original PLAN.md M10.5

  • AWQ safetensors loader deferred. On closer inspection AWQ groups along the in axis while MLX's transpose=false path expects groups along the stored last axis (out). Correct conversion requires transposing packed tensors, unpacking qzeros into biases, and mapping HF param names to Bumblebee names — tractable but additional scope. The from-dense path validates the full defn-integration pipeline end-to-end.
  • Conformance oracle is Emily's own first-run output (same discipline as qwen3_full_test.exs) rather than MLX Python output on AWQ weights.

Full details in PLAN.md §M10.5.

Test plan

  • mix precommit — 263 tests + 66 properties + 1 doctest, 0 failures, credo clean
  • mix test --only qwen3_quant_full — Qwen3-0.6B quantized greedy decode matches pinned reference (~14s after model cache)
  • 26 new unit/property tests across test/emily/quantization/:
    • dequantize_defn_test.exs — element-wise equality with QuantizedWeight.to_dense/1 across all (bits, group_size) combos
    • layers_test.exsquantized_dense/4 transpose=true/false, defn composition
    • transform_test.exs — graph rewrite + state quantize + MLP round-trip under both Nx.Defn.Evaluator and Emily.Compiler

Closes the gap M10 left open: `Native.quantized_matmul` is now
reachable from `Nx.Defn.jit`-traced Axon forward passes, and a
quantized Qwen3-0.6B greedy-decodes end-to-end on Emily through
stock `Bumblebee.Text.generation/4`.

Approach: defn-native dequantize (Option 1 from PLAN.md M10.5).
Rejected Option 2 (fork Evaluator — fragile) and Option 3 (upstream
Nx custom-op hook — too slow). Trade-off is two kernels per matmul
(dequantize + dot) instead of MLX's single fused quantized_matmul;
M11's fast-kernel work closes the gap.

- Emily.Quantization.dequantize_defn/1 — defn-native MLX affine
  dequantize built from Nx.right_shift / bitwise_and / multiply /
  add. Supports bits ∈ {2, 4, 8}; {3, 6} out of scope (cross-u32
  packing). Uses Nx.flatten with negative axes and reshape :auto.
- Emily.Quantization.Layers.quantized_dense/4 — Axon-compatible
  layer op. Pattern-matches on %QuantizedWeight{}; transpose flag
  carried as compile-time constant via deftransform.
- Emily.Quantization.Transform — graph rewriter + state quantizer
  modeled on Axon.Quantization.quantize/2. quantize/3 takes a dense
  Axon model + dense ModelState and returns the quantized pair.
  Lives under test/support/ because Axon is only: :test.
- qwen3_quant_full opt-in conformance — loads dense Qwen3-0.6B,
  quantizes via Transform.quantize/3 (bits=4, group_size=128,
  transpose=true), greedy-decodes 32 tokens, pins continuation text
  as regression gate. ~14s after model cache.
- 26 new unit/property tests across quantization/ covering dequant
  equality, layer op under both Nx.Defn.Evaluator and Emily.Compiler,
  graph-rewrite round-trip, and validation.

Scope reductions from original PLAN.md M10.5:

- AWQ safetensors loader deferred. On closer inspection AWQ groups
  along `in` while MLX transpose=false expects groups along the
  stored last axis (out); correct conversion requires transposing
  packed tensors, unpacking qzeros into biases, and mapping HF
  param names to Bumblebee names. All tractable but additional
  scope. The from-dense path exercises the same defn-integration
  pipeline end-to-end.
- Conformance oracle is Emily's own first-run output (same
  discipline as qwen3_full_test.exs) rather than MLX Python.

mix precommit: 263 tests + 66 properties + 1 doctest, 0 failures,
credo clean. qwen3_quant_full green (opt-in).
@ausimian
ausimian merged commit 8f55aa4 into main Apr 15, 2026
1 check passed
@ausimian
ausimian deleted the m10.5-bumblebee-quantized-inference branch April 15, 2026 08:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant