File size: 4,547 Bytes
de25ff8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ae7f36a
 
 
 
 
 
 
 
 
 
 
 
 
de25ff8
 
 
 
 
 
 
 
ae7f36a
 
 
 
 
 
 
 
 
de25ff8
 
 
 
 
ae7f36a
 
de25ff8
 
 
ae7f36a
 
 
 
 
 
de25ff8
ae7f36a
de25ff8
 
ae7f36a
 
 
 
 
de25ff8
 
ae7f36a
 
 
 
de25ff8
 
ae7f36a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
---
license: apache-2.0
tags: [qhexrt, hexagon, npu, qnn, llm, gated-delta-net]
base_model: Qwen/Qwen3.5-2B
pipeline_tag: text-generation
---

# Qwen3.5-2B — Hexagon NPU (QHexRT) — v81

A prebuilt **QHexRT** bundle of **Qwen/Qwen3.5-2B** (the **Qwen3-Next "GatedDeltaNet" hybrid** lineage)
running on the **Qualcomm Hexagon v81 NPU** (SM8850). text→text.

The text decoder is a **hybrid**: 3 of every 4 layers are a **recurrent gated delta-rule linear attention**
(`Qwen3_5GatedDeltaNet`: a short causal-conv FIFO + an SSM state + gated RMSNorm), and every 4th layer is
**gated softmax attention** (q-proj output gate, per-head q/k RMSNorm, **partial-rotary** M-RoPE θ=1e7). The
runtime drives it with the `qwen3_5_generate` host-op (decode-over-prompt; conv + SSM + KV state carried
host-side), the lm-head as a separate graph.

## Two precision bundles — pick the dir

| dir | precision | decode bin | decode | parity | when to use |
|---|---|---|---|---|---|
| **`v81/`** | **fp16** | 2.76 GB | ~11.0 tok/s | **strict greedy-exact** (suite 10/10) | bit-faithful HF parity |
| **`v81-w8/`** | **W8A16** (decode W8 + fp16 lm-head) | **1.3 GB** | **~14.4 tok/s** | single-prompt greedy-exact 64/64; **length-swept suite 8/10** under a tolerant `greedy_tol` gate | **~half the decode size + ~1.3× faster**; coherent throughout |

Both are device-validated on **SM8850 / v81** (QAIRT 2.47). The W8 decode also **fits a single context** and
avoids the fp16 build's >2 GB large-code-model path. Same `embed_f16` / `lmhead_f16` / `tokenizer` in both.

## What's here

### `v81/` (fp16 — strict parity)
| file | role | ~size |
|---|---|---|
| `qwen3.5-2b-1024.json` | QHexRT manifest (the declarative run plan) | 2 KB |
| `qwen352b_decode_f16.bin` | decode context binary (24 layers, hid 2048, fp16) | 2.76 GB |
| `qwen352b_lmhead_f16.bin` | lm-head context binary (tied embed, fp16) | 1.0 GB |
| `qwen352b_embed_f16.bin` | embedding table (host lookup, fp16) | 1.0 GB |
| `tokenizer.json` | Qwen2-style BPE tokenizer | 20 MB |

### `v81-w8/` (W8A16 — smaller + faster)
| file | role | ~size |
|---|---|---|
| `qwen3.5-2b-1024.json` | QHexRT manifest (references the W8 decode) | 2 KB |
| `qwen352b_decode_w8.bin` | decode context binary (24 layers, **W8 weight-only**) | **1.3 GB** |
| `qwen352b_lmhead_f16.bin` | lm-head context binary (tied embed, fp16) | 1.0 GB |
| `qwen352b_embed_f16.bin` | embedding table (host lookup, fp16) | 1.0 GB |
| `tokenizer.json` | Qwen2-style BPE tokenizer | 20 MB |

Arch-pinned: a v81 binary will not load on another Hexagon arch. The QNN runtime libs come from the QAIRT SDK,
not this repo.

## Run
```bash
# fp16 (strict parity):
hf download runanywhere/qwen3_5_2b_HNPU --include "v81/*" --local-dir q35_2b
adb push q35_2b/v81 /data/local/tmp/wq/qwen35-2b
adb shell "cd /data/local/tmp/wq && LD_LIBRARY_PATH=. \
  ./qhx_generate qwen35-2b/qwen3.5-2b-1024.json libQnnHtp.so libQnnSystem.so qwen35-2b 64 'The capital of France is'"

# W8 (smaller + faster):
hf download runanywhere/qwen3_5_2b_HNPU --include "v81-w8/*" --local-dir q35_2b
adb push q35_2b/v81-w8 /data/local/tmp/wq/qwen35-2b-w8
adb shell "cd /data/local/tmp/wq && LD_LIBRARY_PATH=. \
  ./qhx_generate qwen35-2b-w8/qwen3.5-2b-1024.json libQnnHtp.so libQnnSystem.so qwen35-2b-w8 64 'The capital of France is'"
```
(also stage the QAIRT runtime libs + a `qhx_generate` built with the `qwen3_5_generate` host-op — see caveats.)

## Measured (device: SM8850 / v81, QAIRT 2.47)
- **fp16 (`v81/`):** ~11.0 tok/s; greedy-EXACT vs HF `Qwen/Qwen3.5-2B` (41/41).
- **W8 (`v81-w8/`):** **~14.4 tok/s** (~1.3× fp16); single-prompt greedy-exact **64/64**; length-swept parity
  suite (up to 70% of MAXCTX) **8/10** under a tolerant `greedy_tol` gate (the 2 misses are W8 near-tie
  argmax-drift into coherent-but-different text — the longer-context variants pass, so it is benign quant
  noise, not a context bug).

## Caveats
- **Precision is your choice:** `v81/` fp16 for strict bit-faithful parity, `v81-w8/` for ~half the decode
  size + ~1.3× speed at a *tolerant* (8/10 suite) parity. W8 quantizes only weights — the fp32 SSM scan +
  host conv/SSM/KV state are untouched.
- **W4 is HTP-toolchain-blocked** on v81 — W8 is the quant floor.
- **Text-LLM path only** — the vision tower + MTP head are not exported.
- Needs a `qhx_generate` that includes the **`qwen3_5_generate`** host-op (QHexRT branch `smonga/qwen_fam`).
- Built by the in-repo `forge` pipeline (oracle-gated export → QAIRT-2.47 O3 compile → device greedy gate).