Carbon-X — MLX MXFP4
MLX MXFP4 quantization of HuggingFaceBio/Carbon-X, a
Qwen3ForCausalLM fine-tune (14B dense,
40 layers, hidden 5120, GQA 40/8 heads, standard architecture — no custom
modeling code) extended with a hybrid DNA tokenizer: alongside the normal
Qwen3 BPE vocabulary (151,643 tokens), the embedding table carries 5,515
additional rows for DNA 6-mer tokens (dna_start_id 151669), so the model can
read <dna>ACGT...</dna>-tagged nucleotide sequences as single tokens per
6-base chunk instead of spelling them out letter-by-letter. Runs on Apple
Silicon via mlx-lm.
| Precision | MXFP4 (E2M1 + E8M0 shared scale, group size 32) |
| Bits per weight | 4.250 bpw |
| On-disk size | 7.3 GB |
| Quantized with | uniform MXFP4 via mlx_lm.convert --q-mode mxfp4 |
⚠️ Important: DNA tokenization needs the Python tokenizer
The DNA 6-mer vocabulary (ids 151669–157183) exists only inside the
repo's custom tokenizer.py (HybridDNATokenizer) — it is generated in
Python at load time and is not present in tokenizer.json. This means:
- Plain-text chat / Q&A works exactly as expected through
mlx-lm, LM Studio, or any OpenAI-compatible server using the stock tokenizer — this is what's covered by the verification below. - Structured DNA tasks (variant scoring,
<dna>...</dna>sequence analysis, theskills/carbon-dnaworkflow from the base model) require running the tokenizer withtrust_remote_code=TruesoHybridDNATokenizerbuilds the DNA vocabulary — e.g.transformers.AutoTokenizer.from_pretrained( "sahilchachra/Carbon-X-MXFP4", trust_remote_code=True), or any serving stack (vLLM, etc.) that loads custom tokenizer code. mlx-lm'sload()and LM Studio's indexer do not executetokenizer.py, so a raw<dna>...</dna>string typed into either will silently fall back to byte/BPE tokenization of the literal characters instead of 6-mer DNA tokens — it will not error, but it is not doing genomics inference. - This repo keeps
tokenizer.py,dna_config.json, andtokenizer_config.json(withauto_mapintact) exactly as shipped, so the hybrid tokenizer is available to any Python-side consumer that opts intotrust_remote_code.
Other quantizations of this model
Verification
Text coherence (native mlx-lm, deterministic generation): "What is the
capital of France?" → "The capital of France is Paris."
DNA embedding path — tested by encoding a real reference/variant DNA pair
with the actual HybridDNATokenizer (bypassing the plain tokenizer, which has
no DNA vocab) and feeding the resulting ids directly into each MLX checkpoint
to score the log-likelihood of a single-base substitution:
| Build | ref log-likelihood | alt log-likelihood | Δ (alt − ref) |
|---|---|---|---|
| MXFP4 | −41.94 | −48.44 | −6.50 |
| MXFP8 | −40.90 | −47.65 | −6.75 |
| OptiQ-5bpw | −41.80 | −48.05 | −6.25 |
All three builds agree in sign and magnitude (the single-base substitution is consistently scored as markedly less likely than the reference), confirming the DNA token embeddings and the hybrid vocabulary survive quantization correctly — the DNA-specific weight rows are not degraded or dropped.
LM Studio — all three builds load cleanly via the MLX runtime (lms load)
and were tested through the OpenAI-compatible /v1/chat/completions API at
localhost:1234, producing correct, properly-terminated answers (no runaway
generation). The upstream chat_template.jinja contains {% generation %}/
{% endgeneration %} markers (a transformers-only training-loss-masking
extension); these are no-ops at inference time and were stripped from this
repo's template so LM Studio's Jinja engine (which doesn't recognize them)
doesn't error. The rendered prompt is otherwise byte-identical to the
upstream template.
Usage (mlx-lm)
pip install -U mlx-lm
from mlx_lm import load, generate
model, tokenizer = load("sahilchachra/Carbon-X-MXFP4")
prompt = tokenizer.apply_chat_template(
[{"role": "user", "content": "What is the capital of France?"}],
add_generation_prompt=True, tokenize=False,
)
print(generate(model, tokenizer, prompt=prompt, max_tokens=200))
For DNA-specific tasks, use the base model's skills/carbon-dna tooling or a
trust_remote_code=True transformers/vLLM load — see the
original model card for the ask/compare workflow.
Run in LM Studio
Loads and runs out of the box — the qwen3 architecture is recognized
natively by LM Studio's bundled MLX runtime. Works through both the UI and the
OpenAI-compatible /v1/chat/completions API. This is a reasoning model: it
emits a <think> block before its answer, so give it enough max_tokens
(100+) or the final answer can be truncated while still thinking.
DNA input is not supported through LM Studio — see the warning above; LM Studio uses the plain BPE tokenizer and has no path to the custom 6-mer vocabulary.
Notes & limitations
tie_word_embeddings=false— the ~804M-parameterlm_headis a separate matrix; it is kept at the highest available precision in every build here (8-bit in OptiQ-5bpw, native in MXFP4/MXFP8) since it is the most sensitivity-critical layer for a 157,184-entry vocabulary spanning both text and DNA tokens.- Config fix: upstream
config.jsonstores RoPE theta under a nestedrope_parametersdict (newer transformers config format);mlx-lm0.31.3 expects a flatrope_thetafield. This repo'sconfig.jsoncarries both for compatibility. - Inherits all capabilities and limitations of the base model. See the original model card.
- Quantized by @sahilchachra with MLX. Apache-2.0.