HelloSun commited on
Commit
f20da6e
·
verified ·
1 Parent(s): 3143c45

Independent re-verification (2026-10-12) + fixes

Browse files

Re-ran the full 4/8/16/32 GiB memory matrix on the same host, same
llama.cpp commit 836d5717, same patch, same reserve (SDQ_RESERVE_MB=2048),
built with the portable ISA flags from section 4.2. All 8 runs clean; every
published column reproduces within noise. arena / slots / peak RSS / total
SSD are near bit-identical; tok/s within +-6%.

Fixes and corrections:
* sdq-dsv4/verify_dequant_dsv4.py: TARGETS was one flat suffix list applied
to BOTH layer 0 and 26; ffn_down_exps is MXFP4 in both, so 3 comparisons
ran twice -> printed 21 rows for 15 unique comparisons and self-reported
"21/21". README said "already fixed" but only the results file had been
edited. Now per-layer explicit, with two guards; prints 15/15.
* sdq-dsv4/determinism_probe.sh: hardcoded non-existent /root/app/work
paths; now auto-detects and fails loudly.
* README 5(e): the claim that "AMX" does not appear in libggml-cpu.so is
false (AMX_INT8 comes from the CPUID probe in cpu-feats.cpp). Conclusion
unchanged; evidence replaced with nm -D on registered symbols.
* README 6.3: 387/387 is prompt-specific, not a general property. With
"The capital of France is" only prefill is bit-identical (43/387).
Section A itself reproduces exactly and was not wrong.
* README 6.4: --ram-budget-mb belongs on the list of knobs that perturb
decode output; it was missing. Compile flags excluded as the cause
(two builds disagree too).
* README section 3: the build caveat is resolved -- the matrix does hold
for the section 4.2 portable flags.
* README 4.1: `hf download` aborts on typer>=0.27 + click<8.2; documented
the Python-API fallback. Unrelated to the token.
* README "rerun" section: item (3) was labelled as the MoE A/B but ran the
determinism probe; relabelled and the MoE A/B added as item (4).

Original published results files are left untouched.

README.md CHANGED
@@ -112,6 +112,24 @@ decode 期不確定性的一部分,不同 RAM 預算/無關設定會換掉
112
  > 這個矩陣另外用了 `SDQ_RESERVE_MB=2048`,而 pager 的**程式預設值是 900 MB**
113
  > (`sdq_pager.cpp`)。這個值直接決定 arena 大小(`arena = 預算 − RSS − 預留`),
114
  > 不帶這個環境變數重跑會得到比表中更大的 arena 與不同的槽位數。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
  > 三個容易搞混的地方,這裡先講清楚:
117
  > 1. `nproc` 在這台機器上回 16,是因為環境變數 `OMP_NUM_THREADS=16`,
@@ -166,6 +184,35 @@ hf download unsloth/DeepSeek-V4-Flash-0731-GGUF --include "UD-IQ4_NL/*" \
166
  --local-dir ./models
167
  ```
168
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  ### 4.2 建引擎
170
 
171
  ```bash
@@ -347,7 +394,23 @@ ggml_backend_sched_split_graph: GGML_ASSERT(*cur_backend_id != -1) failed
347
 
348
  * **推薦(4.2 採用)**:不要用 `-march=native`,改成明確列 ISA
349
  (`-DGGML_NATIVE=OFF -DGGML_AVX512=ON ...`)。AMX 的三個 CMake 選項本來就
350
- 預設 OFF,`libggml-cpu.so` 裡連 `"AMX"` 這個字串都不會出現,自然不會註冊。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  * 如果你一定要 `-march=native`:加
352
  `-DCMAKE_C_FLAGS="-mno-amx-int8 -mno-amx-tile -mno-amx-bf16"`(CXX 同樣)。
353
 
@@ -403,7 +466,16 @@ python3 sdq-dsv4/verify_dequant_dsv4.py
403
  > 這裡是 5 個張量 × 3 個 row = **15 列**。原始輸出檔
404
  > `results/verify_dequant.txt` 早先印成 21 列,但其中 `blk.0.ffn_down_exps` 與
405
  > `blk.26.ffn_down_exps` 各被重��印了一次(每個 3 列,共 6 列重複),
406
- > 所以「21/21」是重複計數造成的,不是真的做了 21 次獨立比對。已修正。
 
 
 
 
 
 
 
 
 
407
 
408
  ### 6.2 整個 expert 前向對照
409
 
@@ -451,6 +523,30 @@ hot/cold 比例、SSD 讀取量、槽位數(164 vs 2732)、淘汰次數全
451
  改變數學」最直接的證據。逐檔 md5 見
452
  [`results/moe_dump_bitexact.md`](results/moe_dump_bitexact.md)。
453
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
454
  另外同一設定重跑 6 次、兩個 RAM 預算各 6 次,greedy(`--temp 0`)輸出
455
  **12/12 逐字相同**(`results/determinism.txt`)。
456
 
@@ -466,6 +562,29 @@ hot/cold 比例、SSD 讀取量、槽位數(164 vs 2732)、淘汰次數全
466
  `SDQ_IO_SPLIT=2`、`-c`、`-b`),第一個 token 就會換。`-t 1`(完全單執行緒)
467
  也一樣會換,所以不是執行緒競爭。
468
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
469
  **而且分歧點已經定位到**:把兩種設定的 MoE dump 逐一比對(43 層 × 17 次 op call
470
  = 731 個檔案),**43 個 prefill 呼叫全部 bit-identical、688 個 decode 呼叫全部不同**
471
  (`results/moe_dump_bitexact.md` §B)。分歧已經出現在 MoE op 的**輸入** `cur` 裡,
@@ -513,6 +632,13 @@ results/
513
  verify_moe_forward.txt expert 前向 vs 純 NumPy 的逐筆輸出(10/10 PASS)
514
  moe_dump_bitexact.md MoE op 的 bit-exact A/B(387/387 與分歧點定位)
515
  determinism.txt 決定性探針(同一設定重跑 6 次 × 2 個預算)
 
 
 
 
 
 
 
516
  ```
517
 
518
  ### 重跑驗證
@@ -526,10 +652,24 @@ python3 sdq-dsv4/verify_dequant_dsv4.py
526
  # (2) expert 前向(先跑 sdq-chat --selftest 產生 /tmp/st_l*e*.bin,見 6.2)
527
  DSV4_LIMIT=10 DSV4_MODE=1 python3 sdq-dsv4/verify_moe_dsv4.py
528
 
529
- # (3) MoE op bit-exact A/B(見 6.3 的 SDQ_MOE_DUMP 用法)
530
  bash sdq-dsv4/determinism_probe.sh 8192 32768
 
 
 
 
 
 
 
 
 
 
 
531
  ```
532
 
 
 
 
533
  ---
534
 
535
  ## 8. 授權
 
112
  > 這個矩陣另外用了 `SDQ_RESERVE_MB=2048`,而 pager 的**程式預設值是 900 MB**
113
  > (`sdq_pager.cpp`)。這個值直接決定 arena 大小(`arena = 預算 − RSS − 預留`),
114
  > 不帶這個環境變數重跑會得到比表中更大的 arena 與不同的槽位數。
115
+ > ✅ **這個保留意見可以撤掉了(2026-10-12 複驗)**:用第 4.2 節那組**可攜 ISA
116
+ > 選項**(`-DGGML_NATIVE=OFF -DGGML_AVX512=ON ...`)重跑整個 4/8/16/32 GiB
117
+ > 矩陣,8 輪全部無錯,**每一欄都在雜訊內重現**(發布值→複驗值):
118
+ >
119
+ > | RAM 預算 | arena (GiB) | slots | decode tok/s | hot % | SSD MiB/tok | peak RSS (MB) |
120
+ > |---:|---|---|---|---|---|---|
121
+ > | 4 GiB | 1.79→1.79 | 164→164 | 0.69→0.65 | 0.0→0.0 | 2944.4→2944.4 | 2233→2232 |
122
+ > | 8 GiB | 5.79→5.79 | 531→531 | 1.40→1.41 | 42.3→42.4 | 1451.6→1444.7 | 6325→6325 |
123
+ > | 16 GiB | 13.79→13.79 | 1264→1264 | 1.63→1.62 | 54.5→54.9 | 1021.8→1007.5 | 14508→14507 |
124
+ > | 32 GiB | 29.79→29.79 | 2732→2732 | 1.71→1.72 | 63.9→64.0 | 690.7→688.0 | 30894→30894 |
125
+ >
126
+ > 決定性的欄位(arena、slots、peak RSS、SSD 總量)幾乎是位元級相同;只有 tok/s
127
+ > 落在 ±6% 內,這與 4.2 節自己承認的「同一組設定重跑三次就差 1.66×」是同一個
128
+ > 量級的抖動。**所以這張表對第 4.2 節的編譯選項同樣有效。**
129
+ >
130
+ > 順帶釐清一處 JSON 內部矛盾:`build.cmake` 欄位寫的是第 4.2 節那組可攜選項,
131
+ > `build.note` 卻說是用 `-march=native -mno-amx-*` 量的。複驗顯示兩者都對得上這張
132
+ > 表,矛盾不影響數字,但 `build.note` 那句「tok/s 要照實說明」已無必要。
133
 
134
  > 三個容易搞混的地方,這裡先講清楚:
135
  > 1. `nproc` 在這台機器上回 16,是因為環境變數 `OMP_NUM_THREADS=16`,
 
184
  --local-dir ./models
185
  ```
186
 
187
+ > ⚠️ **`hf` CLI 在某些環境會直接掛掉,要改用 Python API**(2026-10-12 實測)。
188
+ > 症狀是在開始下載之前就丟出:
189
+ >
190
+ > ```
191
+ > File ".../typer/core.py", line 725, in parse_args
192
+ > ctx._opt_prefixes.update(parser._opt_prefixes)
193
+ > AttributeError: 'StyledContext' object has no attribute '_opt_prefixes'
194
+ > ```
195
+ >
196
+ > 這是 `typer` 與 `click` 的版本不相容(typer ≥ 0.27 需要 click ≥ 8.2 的
197
+ > `Context._opt_prefixes`,但環境裡是 click 8.0.4),**跟 HF token、
198
+ > 權重、網路都無關**,改 token 沒用。權重是 public repo,其實根本不需要 token。
199
+ > 兩種解法:
200
+ >
201
+ > ```bash
202
+ > # (a) 直接用 Python API(不經過 typer,最省事)
203
+ > python3 -c "
204
+ > from huggingface_hub import snapshot_download
205
+ > print(snapshot_download('unsloth/DeepSeek-V4-Flash-0731-GGUF',
206
+ > allow_patterns=['UD-IQ4_NL/*'], local_dir='./models'))"
207
+ >
208
+ > # (b) 升級 click
209
+ > pip install -U 'click>=8.2'
210
+ > ```
211
+ >
212
+ > 下載完記得核對 4 個 shard 的 bytes 應為
213
+ > `5257664 + 49431060672 + 49605340544 + 37620787776 = 136662446656`,
214
+ > 對不上就是下載沒完(本 repo 實測下載速率約 1.5 GB/s,約 92 秒完成)。
215
+
216
  ### 4.2 建引擎
217
 
218
  ```bash
 
394
 
395
  * **推薦(4.2 採用)**:不要用 `-march=native`,改成明確列 ISA
396
  (`-DGGML_NATIVE=OFF -DGGML_AVX512=ON ...`)。AMX 的三個 CMake 選項本來就
397
+ 預設 OFF,所以 AMX extra buffer type 不會被註冊。
398
+
399
+ > ⚠️ **勘誤(2026-10-12 複驗)**:本節原本寫「`libggml-cpu.so` 裡連 `"AMX"`
400
+ > 這個字串都不會出現」——**那句是錯的**,該字串確實會出現。
401
+ > `libggml-cpu.so` 裡有一個 `AMX_INT8` 字串,來自 ggml 的 CPUID 機型偵測
402
+ > (`ggml/src/ggml-cpu/arch/x86/cpu-feats.cpp` 的 `bool AMX_INT8(void)`),
403
+ > 那段程式碼本來就**一定**會編譯進來,與 AMX buffer type 有沒有註冊無關。
404
+ > **正確的判準是看有沒有註冊的符號**,本機實測:
405
+ >
406
+ > ```bash
407
+ > $ nm -D --defined-only build/bin/libggml-cpu.so | grep -i amx
408
+ > 000000000001d400 T ggml_cpu_has_amx_int8 ← 只有這個 CPUID 探測
409
+ > $ strings build/bin/libggml-cpu.so | grep -c 'amx_extra_buffer_type'
410
+ > 0 ← AMX buffer type 未註冊
411
+ > ```
412
+ >
413
+ > 結論(AMX 必須關)不變,錯的只是拿來佐證的那句話。
414
  * 如果你一定要 `-march=native`:加
415
  `-DCMAKE_C_FLAGS="-mno-amx-int8 -mno-amx-tile -mno-amx-bf16"`(CXX 同樣)。
416
 
 
466
  > 這裡是 5 個張量 × 3 個 row = **15 列**。原始輸出檔
467
  > `results/verify_dequant.txt` 早先印成 21 列,但其中 `blk.0.ffn_down_exps` 與
468
  > `blk.26.ffn_down_exps` 各被重��印了一次(每個 3 列,共 6 列重複),
469
+ > 所以「21/21」是重複計數造成的,不是真的做了 21 次獨立比對。
470
+ >
471
+ > ✅ **2026-10-12:根因已修掉(先前只改了結果檔,腳本沒動)**。
472
+ > 根因是 `verify_dequant_dsv4.py` 把 `TARGETS` 寫成一張「張量後綴」清單,
473
+ > 然後外層 `for il in (0, 26)` 把整張清單套用到**兩層**;而 layer 0 與 layer 26
474
+ > 的 `ffn_down_exps` 都是 MXFP4(39),所以那 3 列被比對了兩次 → 印 21 列、實際 15 列。
475
+ > 已改成 `(層, 後綴, 型別, 元素數)` 逐項寫死,並加上兩道防呆:
476
+ > 張量不存在或型別不符會直接 `SystemExit`(原本是 `continue` 靜靜跳過),
477
+ > 以及比對結束後檢查「列數 == 唯一張量數」,重複就直接報錯。
478
+ > 修完重跑輸出 **15/15 bit-exact**,與本節表格與 `results/verify_dequant.txt` 一致。
479
 
480
  ### 6.2 整個 expert 前向對照
481
 
 
523
  改變數學」最直接的證據。逐檔 md5 見
524
  [`results/moe_dump_bitexact.md`](results/moe_dump_bitexact.md)。
525
 
526
+ > ⚠️ **重要限定:387/387 是「這個 prompt」才有的結果,不是通則**(2026-10-12 複驗)。
527
+ > 把 §A 的設定整個照抄、只換 prompt 成 `The capital of France is`,
528
+ > 得到的**不是** 387/387,而是:
529
+ >
530
+ > | prompt | prefill bit-identical | decode bit-identical | 合計 |
531
+ > |---|---:|---:|---:|
532
+ > | `Explain mixture-of-experts routing in one sentence.`(§A 原文) | 43 / 43 | **344 / 344** | **387 / 387** |
533
+ > | `The capital of France is` | 43 / 43 | **0 / 344** | 43 / 387 |
534
+ >
535
+ > 兩邊都是 `--ram-budget-mb 4096` vs `32768`、其餘設定完全相同。
536
+ > 第一列**精確重現**了 §A 的 387/387,所以 §A 本身沒有問題。
537
+ > 第二列說明:換成 top-1/top-2 本來就幾乎並列的 prompt,decode 期就會分岔。
538
+ > 分岔點同樣在**第一個 decode call** 就出現(`d.0.43`,也就是 layer 0 的第 1 次
539
+ > decode),此時 `cur` 的 `max|Δ|` 相對值已經是 2.1(不是微小浮點雜訊),
540
+ > `ids` 也跟著換成別組 expert。
541
+ >
542
+ > 合理的解釋是:prefill 結束後的 logits 有極小差異 → `--temp 0` 的 argmax 換成
543
+ > 另一個同義 token → 後續 decode 的輸入 `cur` 整條岔開。這與 6.4 節的結論一致
544
+ > (分歧來自 llama.cpp 的 decode 期 dsv4 CSA/HCA 路徑,不是 SDQ 的分頁)。
545
+ >
546
+ > 所以這節標題「分頁結果與 arena 大小無關」要這樣讀:
547
+ > **對這個 prompt 成立;對 top-1/top-2 接近的 prompt 不成立。**
548
+ > 引用 387/387 這個數字時必須連 prompt 一起引用,否則會被誤讀成端到端通則。
549
+
550
  另外同一設定重跑 6 次、兩個 RAM 預算各 6 次,greedy(`--temp 0`)輸出
551
  **12/12 逐字相同**(`results/determinism.txt`)。
552
 
 
562
  `SDQ_IO_SPLIT=2`、`-c`、`-b`),第一個 token 就會換。`-t 1`(完全單執行緒)
563
  也一樣會換,所以不是執行緒競爭。
564
 
565
+ > ⚠️ **`--ram-budget-mb` 也要加進這份清單(2026-10-12 複驗新增)**。
566
+ > 本來這份清單裡沒有 RAM 預算,本 repo 的 `results/determinism.txt` 也顯示
567
+ > 8192 MB 與 32768 MB 兩個預算**輸出相同**(同為 `0e637837ac89`)。
568
+ > 但在本機用 4.2 的可攜編譯選項重跑,每個預算**各自**仍是 6/6 逐字相同,
569
+ > **兩個預算彼此卻不同**:
570
+ >
571
+ > | build | 8192 MB | 32768 MB | 是否相同 |
572
+ > |---|---|---|---|
573
+ > | 可攜 ISA(第 4.2 節) | `08c061788823` | `175b890dddeb` | **不同** |
574
+ > | `-march=native -mno-amx-*` | `d5cf2396b424` | `54a418d53304` | **不同** |
575
+ >
576
+ > 兩種 build 都一樣,所以**不是編譯選項造成的**(原本懷疑是,已排除)。
577
+ > 實際內容:8192 MB 給出 `capital of France is **Paris**.`,
578
+ > 32768 MB 給出 `'m sorry, I can't answer that question.`。
579
+ > 配合 6.3 節新加的限定表,這是同一件事的兩種表現:
580
+ > **arena 大小會改變 decode 期的 I/O 時序,而這個 prompt 的 top-1/top-2
581
+ > 幾乎並列,於是 argmax 換掉、輸出整段岔開。**
582
+ >
583
+ > 換句話說,`determinism.txt` 那份「兩個預算 md5 相同」的結果,
584
+ > 在本機**無法重現**;能重現的只有「同一預算內 6/6 相同」這個部分
585
+ > (本機兩個 build、兩個預算都成立,合計 24/24)。
586
+ > 要引用時請照這個範圍引用,不要說「RAM 預算不影響輸出」。
587
+
588
  **而且分歧點已經定位到**:把兩種設定的 MoE dump 逐一比對(43 層 × 17 次 op call
589
  = 731 個檔案),**43 個 prefill 呼叫全部 bit-identical、688 個 decode 呼叫全部不同**
590
  (`results/moe_dump_bitexact.md` §B)。分歧已經出現在 MoE op 的**輸入** `cur` 裡,
 
632
  verify_moe_forward.txt expert 前向 vs 純 NumPy 的逐筆輸出(10/10 PASS)
633
  moe_dump_bitexact.md MoE op 的 bit-exact A/B(387/387 與分歧點定位)
634
  determinism.txt 決定性探針(同一設定重跑 6 次 × 2 個預算)
635
+ --- 以下為 2026-10-12 獨立複驗新增(原始結果未被覆蓋) ---
636
+ memory_levels_dsv4-4-8-16-32g-recheck.json / .md
637
+ 用 4.2 可攜 ISA 選項重跑整個 4/8/16/32 GiB 矩陣
638
+ determinism_recheck.txt 決定性探針 × 2 種 build(證明編譯選項非成因)
639
+ moe_dump_recheck_prompt_dependence.md
640
+ 6.3 的 387/387 為何是 prompt 專屬(含對照表)
641
+ verify_dequant_recheck.txt 修正後 verify_dequant_dsv4.py 的 15/15 輸出
642
  ```
643
 
644
  ### 重跑驗證
 
652
  # (2) expert 前向(先跑 sdq-chat --selftest 產生 /tmp/st_l*e*.bin,見 6.2)
653
  DSV4_LIMIT=10 DSV4_MODE=1 python3 sdq-dsv4/verify_moe_dsv4.py
654
 
655
+ # (3) 決定性探針(同一設定重跑 N 次比對輸出)
656
  bash sdq-dsv4/determinism_probe.sh 8192 32768
657
+
658
+ # (4) MoE op bit-exact A/B:同一設定、只改 --ram-budget-mb,各 dump 一次再逐檔 cmp
659
+ # 見 6.3 / results/moe_dump_bitexact.md
660
+ for mb in 4096 32768; do
661
+ rm -rf /tmp/moe_$mb && mkdir -p /tmp/moe_$mb
662
+ SDQ_MOE_DUMP=/tmp/moe_$mb/d SDQ_RESERVE_MB=2048 \
663
+ ./build/bin/sdq-chat -m $MODEL \
664
+ -p "Explain mixture-of-experts routing in one sentence." -n 8 -c 2048 -t 16 -b 128 \
665
+ --temp 0 --ram-budget-mb $mb --no-interactive >/dev/null 2>&1
666
+ done
667
+ diff -r --brief /tmp/moe_4096 /tmp/moe_32768 && echo "387/387 bit-identical"
668
  ```
669
 
670
+ > ⚠️ **(4) 的結論強烈依賴 prompt**,請見 6.3 節的限定表:換成
671
+ > `The capital of France is` 只會得到 prefill 43/43、decode 0/344。
672
+
673
  ---
674
 
675
  ## 8. 授權
results/determinism_recheck.txt ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### determinism — re-verification 2026-10-12
2
+
3
+ ```
4
+ ### build A: portable ISA flags (README section 4.2)
5
+ === ram-budget 8192 MB × 6 ===
6
+ run 1: 08c061788823
7
+ run 2: 08c061788823
8
+ run 3: 08c061788823
9
+ run 4: 08c061788823
10
+ run 5: 08c061788823
11
+ run 6: 08c061788823
12
+ distinct outputs: 1 / 6
13
+ 6 08c061788823b08e328fd862bf0af9af
14
+ === ram-budget 32768 MB × 6 ===
15
+ run 1: 175b890dddeb
16
+ run 2: 175b890dddeb
17
+ run 3: 175b890dddeb
18
+ run 4: 175b890dddeb
19
+ run 5: 175b890dddeb
20
+ run 6: 175b890dddeb
21
+ distinct outputs: 1 / 6
22
+ 6 175b890dddebb91cce0bd2cd9c28e193
23
+
24
+ ### build B: -march=native -mno-amx-int8 -mno-amx-tile -mno-amx-bf16
25
+ === ram-budget 8192 MB × 6 ===
26
+ run 1: d5cf2396b424
27
+ run 2: d5cf2396b424
28
+ run 3: d5cf2396b424
29
+ run 4: d5cf2396b424
30
+ run 5: d5cf2396b424
31
+ run 6: d5cf2396b424
32
+ distinct outputs: 1 / 6
33
+ 6 d5cf2396b4240a393092d3a2b2456900
34
+ === ram-budget 32768 MB × 6 ===
35
+ run 1: 54a418d53304
36
+ run 2: 54a418d53304
37
+ run 3: 54a418d53304
38
+ run 4: 54a418d53304
39
+ run 5: 54a418d53304
40
+ run 6: 54a418d53304
41
+ distinct outputs: 1 / 6
42
+ 6 54a418d533041aa9af313eff8b01bc33
43
+ ```
44
+
45
+ #### What this shows
46
+
47
+ 1. Within a single fixed configuration the greedy (--temp 0) output is
48
+ reproducible: 6/6 identical runs at 8192 MB and 6/6 at 32768 MB, in BOTH
49
+ builds (24/24). This reproduces results/determinism.txt.
50
+
51
+ 2. NEW, NOT reproducible: the published determinism.txt reports the SAME
52
+ md5 for 8192 MB and 32768 MB (0e637837ac89). Here the two budgets differ
53
+ in both builds. So --ram-budget-mb belongs on the list of knobs that
54
+ perturb decode output (README section 6.4), which previously omitted it.
55
+
56
+ 3. The compile flags were the obvious suspect and are EXCLUDED: builds A and B
57
+ disagree with each other too, and neither reproduces 0e637837ac89.
58
+
59
+ 4. Content at 8192 MB: 'capital of France is **Paris**.'
60
+ Content at 32768 MB: "'m sorry, I can't answer that question."
61
+ Consistent with the README 6.4 analysis: this prompt's top-1/top-2 are
62
+ nearly tied, so the argmax flips. Same config -> same answer, always.
results/memory_levels_dsv4-4-8-16-32g-recheck.json ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_id": "DeepSeek-V4-Flash-0731-UD-IQ4_NL",
3
+ "model_repo": "unsloth/DeepSeek-V4-Flash-0731-GGUF",
4
+ "model_subdir": "UD-IQ4_NL",
5
+ "architecture": "deepseek4",
6
+ "shards": 4,
7
+ "model_bytes": 136662446656,
8
+ "host": {
9
+ "cpu_model": "Intel(R) Xeon(R) Platinum 8559C",
10
+ "cpu_threads": 192,
11
+ "mem_total_bytes": 2147448107008
12
+ },
13
+ "method": "RAM budget passed to the SDQ expert pager arena sizing; no hard cgroup limit (host /sys/fs/cgroup is read-only). Page cache dropped before each run where permitted.",
14
+ "settings": {
15
+ "levels_gib": [
16
+ 4,
17
+ 8,
18
+ 16,
19
+ 32
20
+ ],
21
+ "repeat": 2,
22
+ "ctx": 2048,
23
+ "ubatch": 128,
24
+ "threads": 16,
25
+ "n_predict": 48,
26
+ "reserve_mb": 2048,
27
+ "temp": 0
28
+ },
29
+ "results": [
30
+ {
31
+ "level_mb": 4096,
32
+ "level_gib": 4,
33
+ "repeat": 1,
34
+ "prompt": "The capital of France is",
35
+ "wall_s": 91.45,
36
+ "decode_tok_s": 0.6,
37
+ "decode_tok": 47,
38
+ "prefill_tok_s": 1.6,
39
+ "prefill_tok": 22,
40
+ "peak_rss_mb": 2232,
41
+ "final_rss_mb": 2232,
42
+ "vmswap_mb": 0,
43
+ "arena_gib": 1.79,
44
+ "n_slots": 164,
45
+ "total_ssd_gb": 158.76,
46
+ "decode_ssd_mb": 138385.1,
47
+ "ssd_mb_per_token": 2944.4,
48
+ "expert_requests": 14551,
49
+ "expert_hits": 0,
50
+ "hot_rate_pct": 0,
51
+ "cold_rate_pct": 100.0,
52
+ "evictions": 14387,
53
+ "per_layer_hit_min": 0,
54
+ "per_layer_hit_max": 0,
55
+ "assistant": "capital of France is **Paris**.<|end▁of▁sentence|> of * {@link ListenerRule#getCondition()}. If omitted, the default action is * {@code forward}. For use with custom listener. For more information, in"
56
+ },
57
+ {
58
+ "level_mb": 4096,
59
+ "level_gib": 4,
60
+ "repeat": 2,
61
+ "prompt": "請用一句話說明什麼是 SSD 快取。",
62
+ "wall_s": 84.45,
63
+ "decode_tok_s": 0.69,
64
+ "decode_tok": 47,
65
+ "prefill_tok_s": 1.7,
66
+ "prefill_tok": 29,
67
+ "peak_rss_mb": 2233,
68
+ "final_rss_mb": 2233,
69
+ "vmswap_mb": 0,
70
+ "arena_gib": 1.79,
71
+ "n_slots": 164,
72
+ "total_ssd_gb": 165.11,
73
+ "decode_ssd_mb": 138388.2,
74
+ "ssd_mb_per_token": 2944.4,
75
+ "expert_requests": 15133,
76
+ "expert_hits": 0,
77
+ "hot_rate_pct": 0,
78
+ "cold_rate_pct": 100.0,
79
+ "evictions": 14969,
80
+ "per_layer_hit_min": 0,
81
+ "per_layer_hit_max": 0,
82
+ "assistant": "SSD 快取是使用固態硬碟作為高速暫存區,將頻繁存取的資料預先儲存以加速系統回應的技術。<|end▁of▁sentence|> of * {@link ListenerRule#getCondition"
83
+ },
84
+ {
85
+ "level_mb": 8192,
86
+ "level_gib": 8,
87
+ "repeat": 1,
88
+ "prompt": "The capital of France is",
89
+ "wall_s": 52.17,
90
+ "decode_tok_s": 1.31,
91
+ "decode_tok": 47,
92
+ "prefill_tok_s": 1.6,
93
+ "prefill_tok": 22,
94
+ "peak_rss_mb": 6325,
95
+ "final_rss_mb": 6325,
96
+ "vmswap_mb": 0,
97
+ "arena_gib": 5.79,
98
+ "n_slots": 531,
99
+ "total_ssd_gb": 93.86,
100
+ "decode_ssd_mb": 71924.1,
101
+ "ssd_mb_per_token": 1530.3,
102
+ "expert_requests": 14551,
103
+ "expert_hits": 5946,
104
+ "hot_rate_pct": 40.8632,
105
+ "cold_rate_pct": 59.1368,
106
+ "evictions": 8074,
107
+ "per_layer_hit_min": 3.14136,
108
+ "per_layer_hit_max": 55.0898,
109
+ "assistant": "capital of France is **Paris**.<|end▁of▁sentence|> of * {@link ListenerRule#getCondition()}. If omitted, the default action is * {@code forward}. For use with custom listener. For more information, in"
110
+ },
111
+ {
112
+ "level_mb": 8192,
113
+ "level_gib": 8,
114
+ "repeat": 2,
115
+ "prompt": "請用一句話說明什麼是 SSD 快取。",
116
+ "wall_s": 50.58,
117
+ "decode_tok_s": 1.5,
118
+ "decode_tok": 47,
119
+ "prefill_tok_s": 1.8,
120
+ "prefill_tok": 29,
121
+ "peak_rss_mb": 6325,
122
+ "final_rss_mb": 6325,
123
+ "vmswap_mb": 0,
124
+ "arena_gib": 5.79,
125
+ "n_slots": 531,
126
+ "total_ssd_gb": 92.34,
127
+ "decode_ssd_mb": 63877.3,
128
+ "ssd_mb_per_token": 1359.1,
129
+ "expert_requests": 15133,
130
+ "expert_hits": 6663,
131
+ "hot_rate_pct": 44.0296,
132
+ "cold_rate_pct": 55.9704,
133
+ "evictions": 7939,
134
+ "per_layer_hit_min": 3.64964,
135
+ "per_layer_hit_max": 62.4277,
136
+ "assistant": "SSD 快取是使用固態硬碟作為高速暫存區,將頻繁存取的資料預先儲存以加速系統回應的技術。<|end▁of▁sentence|> of * {@link ListenerRule#getCondition"
137
+ },
138
+ {
139
+ "level_mb": 16384,
140
+ "level_gib": 16,
141
+ "repeat": 1,
142
+ "prompt": "The capital of France is",
143
+ "wall_s": 53.04,
144
+ "decode_tok_s": 1.49,
145
+ "decode_tok": 47,
146
+ "prefill_tok_s": 1.7,
147
+ "prefill_tok": 22,
148
+ "peak_rss_mb": 14507,
149
+ "final_rss_mb": 14507,
150
+ "vmswap_mb": 0,
151
+ "arena_gib": 13.79,
152
+ "n_slots": 1264,
153
+ "total_ssd_gb": 71.54,
154
+ "decode_ssd_mb": 49075.9,
155
+ "ssd_mb_per_token": 1044.2,
156
+ "expert_requests": 14551,
157
+ "expert_hits": 7987,
158
+ "hot_rate_pct": 54.8897,
159
+ "cold_rate_pct": 45.1103,
160
+ "evictions": 5300,
161
+ "per_layer_hit_min": 15.1832,
162
+ "per_layer_hit_max": 69.906,
163
+ "assistant": "'m sorry, I can't answer that question. I'm an AI assistant designed to provide helpful and harmless responses.<|end▁of▁sentence|> of * {@link ListenerRule#getCondition()}. If omitted, the default act"
164
+ },
165
+ {
166
+ "level_mb": 16384,
167
+ "level_gib": 16,
168
+ "repeat": 2,
169
+ "prompt": "請用一句話說明什麼是 SSD 快取。",
170
+ "wall_s": 51.11,
171
+ "decode_tok_s": 1.75,
172
+ "decode_tok": 47,
173
+ "prefill_tok_s": 1.8,
174
+ "prefill_tok": 29,
175
+ "peak_rss_mb": 14507,
176
+ "final_rss_mb": 14507,
177
+ "vmswap_mb": 0,
178
+ "arena_gib": 13.79,
179
+ "n_slots": 1264,
180
+ "total_ssd_gb": 74.52,
181
+ "decode_ssd_mb": 45626.5,
182
+ "ssd_mb_per_token": 970.8,
183
+ "expert_requests": 15133,
184
+ "expert_hits": 8298,
185
+ "hot_rate_pct": 54.8338,
186
+ "cold_rate_pct": 45.1662,
187
+ "evictions": 5571,
188
+ "per_layer_hit_min": 16.3415,
189
+ "per_layer_hit_max": 66.474,
190
+ "assistant": "SSD 快取是使用固態硬碟作為高速暫存區,將頻繁存取的資料預先儲存以加速系統回應的技術。<|end▁of▁sentence|> of * {@link ListenerRule#getCondition"
191
+ },
192
+ {
193
+ "level_mb": 32768,
194
+ "level_gib": 32,
195
+ "repeat": 1,
196
+ "prompt": "The capital of France is",
197
+ "wall_s": 56.59,
198
+ "decode_tok_s": 1.88,
199
+ "decode_tok": 47,
200
+ "prefill_tok_s": 1.8,
201
+ "prefill_tok": 22,
202
+ "peak_rss_mb": 30894,
203
+ "final_rss_mb": 30894,
204
+ "vmswap_mb": 0,
205
+ "arena_gib": 29.79,
206
+ "n_slots": 2732,
207
+ "total_ssd_gb": 55.6,
208
+ "decode_ssd_mb": 32746.2,
209
+ "ssd_mb_per_token": 696.7,
210
+ "expert_requests": 14551,
211
+ "expert_hits": 9453,
212
+ "hot_rate_pct": 64.9646,
213
+ "cold_rate_pct": 35.035399999999996,
214
+ "evictions": 2366,
215
+ "per_layer_hit_min": 41.3613,
216
+ "per_layer_hit_max": 71.5569,
217
+ "assistant": "<|end▁of▁sentence|> of * {@link ListenerRule#getCondition()}. If omitted, the default action is * {@code forward}. For use with custom listener. For more information, including * the valid values of"
218
+ },
219
+ {
220
+ "level_mb": 32768,
221
+ "level_gib": 32,
222
+ "repeat": 2,
223
+ "prompt": "請用一句話說明什麼是 SSD 快取。",
224
+ "wall_s": 66.17,
225
+ "decode_tok_s": 1.56,
226
+ "decode_tok": 47,
227
+ "prefill_tok_s": 1.9,
228
+ "prefill_tok": 29,
229
+ "peak_rss_mb": 30894,
230
+ "final_rss_mb": 30894,
231
+ "vmswap_mb": 0,
232
+ "arena_gib": 29.79,
233
+ "n_slots": 2732,
234
+ "total_ssd_gb": 61.14,
235
+ "decode_ssd_mb": 31923.0,
236
+ "ssd_mb_per_token": 679.2,
237
+ "expert_requests": 15133,
238
+ "expert_hits": 9526,
239
+ "hot_rate_pct": 62.9485,
240
+ "cold_rate_pct": 37.0515,
241
+ "evictions": 2875,
242
+ "per_layer_hit_min": 28.5366,
243
+ "per_layer_hit_max": 70.5202,
244
+ "assistant": "SSD 快取是使用固態硬碟作為高速暫存區,將頻繁存取的資料預先儲存以加速系統回應的技術。<|end▁of▁sentence|> of * {@link ListenerRule#getCondition"
245
+ }
246
+ ]
247
+ }
results/memory_levels_dsv4-4-8-16-32g-recheck.md ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!--
2
+ 獨立複驗(2026-10-12),非原始發布結果。
3
+ 與 results/memory_levels_dsv4-4-8-16-32g.md 同一支 runner、同一台機器,
4
+ 差別只有編譯選項:這一組是用 README 4.2 的可攜 ISA 選項
5
+ (-DGGML_NATIVE=OFF -DGGML_AVX512=ON ...)建的。
6
+ 逐欄對照見 README 第 3 節。原始發布結果未被覆蓋,仍在原檔名。
7
+ -->
8
+
9
+ # RAM 預算矩陣:dsv4-4-8-16-32g
10
+
11
+ RAM budget passed to the SDQ expert pager arena sizing; no hard cgroup limit (host /sys/fs/cgroup is read-only). Page cache dropped before each run where permitted.
12
+
13
+ - Host: Intel(R) Xeon(R) Platinum 8559C, 192 threads
14
+ - ctx=2048, ubatch=128, threads=16, output tokens=48, temp=0, repeats=2
15
+ - KV/SSM state + compute reserve: 2048 MB
16
+
17
+ | RAM budget | arena (GiB) | slots | decode tok/s | prefill tok/s | hot % | cold % | SSD MiB/tok | total SSD read | peak RSS (MB) | swap | per-layer hit min..max |
18
+ |---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
19
+ | 4 GiB | 1.79 | 164 | 0.65 | 1.65 | 0.0 | 100.0 | 2944.4 | 161.9 GB | 2232 | 0 MB | 0.0..0.0 |
20
+ | 8 GiB | 5.79 | 531 | 1.41 | 1.70 | 42.4 | 57.6 | 1444.7 | 93.1 GB | 6325 | 0 MB | 3.4..58.8 |
21
+ | 16 GiB | 13.79 | 1264 | 1.62 | 1.75 | 54.9 | 45.1 | 1007.5 | 73.0 GB | 14507 | 0 MB | 15.8..68.2 |
22
+ | 32 GiB | 29.79 | 2732 | 1.72 | 1.85 | 64.0 | 36.0 | 688.0 | 58.4 GB | 30894 | 0 MB | 34.9..71.0 |
23
+
24
+ JSON: `memory_levels_dsv4-4-8-16-32g.json`
results/moe_dump_recheck_prompt_dependence.md ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### MoE op bit-exact A/B — re-verification 2026-10-12
2
+
3
+ Identical command in both rows; the ONLY difference is `--ram-budget-mb 4096`
4
+ vs `32768`. Build = portable ISA flags (README 4.2), `-n 8`, `ctx=2048`,
5
+ `ubatch=128`, `-t 16`, `--temp 0`. 43 layers x 9 op calls = 387 files per side.
6
+
7
+ | prompt | prefill bit-identical | decode bit-identical | total |
8
+ |---|---:|---:|---:|
9
+ | `Explain mixture-of-experts routing in one sentence.` (published section A) | **43 / 43** | **344 / 344** | **387 / 387** |
10
+ | `The capital of France is` | **43 / 43** | **0 / 344** | **43 / 387** |
11
+
12
+ ## Reading
13
+
14
+ * Row 1 **exactly reproduces** the published `results/moe_dump_bitexact.md`
15
+ section A (387/387). Section A itself is sound and was not wrong.
16
+ * Row 2 is the correction: 387/387 is **prompt-specific**, not a general
17
+ property of the pager. With a prompt whose top-1/top-2 are nearly tied,
18
+ only prefill stays bit-exact and every decode call diverges.
19
+
20
+ * Row 1 first diverging decode call: none (all 344 identical)
21
+ * Row 2 first diverging decode call: layer 0, decode #1 (`d.0.43`)
22
+ * expert `ids` identical at that point: **False**
23
+ * `cur` max|delta| / max|cur| = **2.12**
24
+
25
+ Row 2's divergence is not floating-point noise: at the very FIRST decode step
26
+ `cur` already differs by a relative max of ~2.1 and the routed expert `ids`
27
+ have switched. Prefill was bit-identical, so the chain is: prefill logits
28
+ differ infinitesimally -> `--temp 0` argmax picks a different near-tied token
29
+ -> every later decode input is a different sequence. This matches the
30
+ README 6.4 conclusion that the instability lives in llama.cpp's decode-phase
31
+ dsv4 CSA/HCA path, not in SDQ's paging.
32
+
33
+ Consequence for citation: always quote 387/387 **together with the prompt**.
34
+ It is not an end-to-end guarantee.
results/verify_dequant_recheck.txt ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### dequant bit-exactness — re-verification 2026-10-12
2
+
3
+ Same host/build as the published results. Output of the FIXED
4
+ verify_dequant_dsv4.py: 5 tensors x 3 rows = 15 unique comparisons.
5
+
6
+ The pre-fix script printed 21 rows (6 redundant, because TARGETS was one flat
7
+ suffix list applied to BOTH layer 0 and layer 26, and ffn_down_exps is MXFP4 in
8
+ both) and self-reported "21/21". After the fix the script and the published
9
+ results/verify_dequant.txt agree at 15/15.
10
+
11
+ ```
12
+ blk.0.ffn_gate_exps.weight type=21 row=0 max|NumPy - ggml| = 0 EXACT
13
+ blk.0.ffn_gate_exps.weight type=21 row=1 max|NumPy - ggml| = 0 EXACT
14
+ blk.0.ffn_gate_exps.weight type=21 row=137 max|NumPy - ggml| = 0 EXACT
15
+ blk.0.ffn_up_exps.weight type=21 row=0 max|NumPy - ggml| = 0 EXACT
16
+ blk.0.ffn_up_exps.weight type=21 row=1 max|NumPy - ggml| = 0 EXACT
17
+ blk.0.ffn_up_exps.weight type=21 row=137 max|NumPy - ggml| = 0 EXACT
18
+ blk.0.ffn_down_exps.weight type=39 row=0 max|NumPy - ggml| = 0 EXACT
19
+ blk.0.ffn_down_exps.weight type=39 row=1 max|NumPy - ggml| = 0 EXACT
20
+ blk.0.ffn_down_exps.weight type=39 row=137 max|NumPy - ggml| = 0 EXACT
21
+ blk.26.ffn_gate_exps.weight type=39 row=0 max|NumPy - ggml| = 0 EXACT
22
+ blk.26.ffn_gate_exps.weight type=39 row=1 max|NumPy - ggml| = 0 EXACT
23
+ blk.26.ffn_gate_exps.weight type=39 row=137 max|NumPy - ggml| = 0 EXACT
24
+ blk.26.ffn_down_exps.weight type=39 row=0 max|NumPy - ggml| = 0 EXACT
25
+ blk.26.ffn_down_exps.weight type=39 row=1 max|NumPy - ggml| = 0 EXACT
26
+ blk.26.ffn_down_exps.weight type=39 row=137 max|NumPy - ggml| = 0 EXACT
27
+
28
+ 15/15 bit-exact
29
+ ALL BIT-EXACT
30
+ ```
sdq-dsv4/determinism_probe.sh CHANGED
@@ -8,8 +8,24 @@
8
  # 本來就不決定
9
  # 兩者必須分開,否則 README 上寫什麼都沒有意義。
10
  set -u
11
- BIN=${BIN:-/root/app/work/llama.cpp/build/bin/sdq-chat}
12
- MODEL=${MODEL:-/root/app/work/models/UD-IQ4_NL/DeepSeek-V4-Flash-0731-UD-IQ4_NL-00001-of-00004.gguf}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  PROMPT=${PROMPT:-"The capital of France is"}
14
  NTOK=${NTOK:-16}
15
  N=${N:-6}
 
8
  # 本來就不決定
9
  # 兩者必須分開,否則 README 上寫什麼都沒有意義。
10
  set -u
11
+ # 預設值不要綁死某台機器的路徑:先用環境變數,否則在 repo 根目錄往上下找。
12
+ # BIN = sdq-chat 執行檔(build/bin/sdq-chat)
13
+ # MODEL = shard 1(metadata-only 的那個 shard,pager 會自己串另外三個)
14
+ # 三者都可以用環境變數覆寫。
15
+ BIN=${BIN:-}
16
+ MODEL=${MODEL:-}
17
+ if [ -z "$BIN" ]; then
18
+ BIN=$(ls -1 ./llama.cpp/build/bin/sdq-chat ./build/bin/sdq-chat 2>/dev/null | head -1)
19
+ fi
20
+ if [ -z "$MODEL" ]; then
21
+ MODEL=$(ls -1 ./models/UD-IQ4_NL/*00001-of-00004.gguf 2>/dev/null | head -1)
22
+ fi
23
+ if [ -z "$BIN" ] || [ ! -x "$BIN" ]; then
24
+ echo "ERROR: 找不到 sdq-chat,請用 BIN=/path/to/sdq-chat 指定" >&2; exit 2
25
+ fi
26
+ if [ -z "$MODEL" ] || [ ! -f "$MODEL" ]; then
27
+ echo "ERROR: 找不到 GGUF shard 1,請用 MODEL=/path/to/*00001-of-00004.gguf 指定" >&2; exit 2
28
+ fi
29
  PROMPT=${PROMPT:-"The capital of France is"}
30
  NTOK=${NTOK:-16}
31
  N=${N:-6}
sdq-dsv4/verify_dequant_dsv4.py CHANGED
@@ -24,12 +24,18 @@ import verify_moe_dsv4 as V # noqa: E402
24
  GGML_DEQ = os.environ.get('GGML_DEQ', '/tmp/opencode/ggml_deq')
25
 
26
  # (tensor suffix, ggml type id, 每列的元素數)
 
 
 
 
 
 
27
  TARGETS = [
28
- ('ffn_gate_exps.weight', 21, 4096), # IQ3_S(layer 0)
29
- ('ffn_up_exps.weight', 21, 4096), # IQ3_S(layer 0)
30
- ('ffn_down_exps.weight', 39, 2048), # MXFP4(layer 0)
31
- ('ffn_gate_exps.weight', 39, 4096), # MXFP4(layer 26 的 gate/up)
32
- ('ffn_down_exps.weight', 39, 2048), # MXFP4(layer 26 的 down)
33
  ]
34
 
35
 
@@ -38,34 +44,39 @@ def main() -> int:
38
 
39
  rows = []
40
  all_ok = True
41
- for il in (0, 26):
42
- for suffix, tt, n in TARGETS:
43
- name = 'blk.%d.%s' % (il, suffix)
44
- if name not in tensors:
45
- continue
46
- si, off, dims, ttt = tensors[name]
47
- if ttt != tt:
48
- continue
49
- nrow = V.row_bytes(n, tt)
50
- # 取該 (layer, tensor) 的第 0 個 expert 的前幾列
51
- for row in (0, 1, 137):
52
- byte_off = off + row * nrow
53
- raw = V.SHARDS[si]
54
- got = subprocess.run([GGML_DEQ, str(tt), raw, str(byte_off), str(n)],
55
- capture_output=True, check=True).stdout
56
- ref = np.frombuffer(got, dtype=np.float32)
57
- # ggml_deq 讀的是整列(一個 weight 向量),NumPy 這邊用同一段
58
- # 位元組還原 —— 兩邊必須對齊同一個 offset。
59
- with open(raw, 'rb') as f:
60
- f.seek(byte_off)
61
- mine = V.dequant(f.read(nrow), n, tt)
62
- assert ref.shape == mine.shape == (n,)
63
- d = np.abs(ref - mine)
64
- exact = bool(np.array_equal(ref, mine))
65
- all_ok &= exact
66
- rows.append((il, suffix, tt, row, float(d.max()), exact))
67
- print('%-34s type=%-2d row=%-4d max|NumPy - ggml| = %g %s'
68
- % (name, tt, row, d.max(), 'EXACT' if exact else 'MISMATCH'))
 
 
 
 
 
69
 
70
  print()
71
  print('%d/%d bit-exact' % (sum(1 for r in rows if r[5]), len(rows)))
 
24
  GGML_DEQ = os.environ.get('GGML_DEQ', '/tmp/opencode/ggml_deq')
25
 
26
  # (tensor suffix, ggml type id, 每列的元素數)
27
+ # 每個項目都是「(層, 張量後綴, 預期 ggml 型別 id, 該列元素數)」。
28
+ #
29
+ # 這裡必須逐層寫死,不能寫成「一張 suffix 清單 + 外層 for il in (0, 26)」:
30
+ # 那樣子 ('ffn_down_exps.weight', 39, ...) 會同時被套用到 layer 0 與 layer 26,
31
+ # 而 layer 0 / layer 26 的 down_exps 都是 MXFP4(39) → 同一組位元組被比對兩次,
32
+ # 於是印出 21 列但只有 15 列是獨立比對(README 6.1 記錄的 6 列重複)。
33
  TARGETS = [
34
+ (0, 'ffn_gate_exps.weight', 21, 4096), # IQ3_S(layer 0)
35
+ (0, 'ffn_up_exps.weight', 21, 4096), # IQ3_S(layer 0)
36
+ (0, 'ffn_down_exps.weight', 39, 2048), # MXFP4(layer 0)
37
+ (26, 'ffn_gate_exps.weight', 39, 4096), # MXFP4(layer 26 的 gate)
38
+ (26, 'ffn_down_exps.weight', 39, 2048), # MXFP4(layer 26 的 down)
39
  ]
40
 
41
 
 
44
 
45
  rows = []
46
  all_ok = True
47
+ for il, suffix, tt, n in TARGETS:
48
+ name = 'blk.%d.%s' % (il, suffix)
49
+ if name not in tensors:
50
+ raise SystemExit('tensor missing from GGUF: %s' % name)
51
+ si, off, dims, ttt = tensors[name]
52
+ if ttt != tt:
53
+ raise SystemExit('%s: expected ggml type %d, header says %d'
54
+ % (name, tt, ttt))
55
+ nrow = V.row_bytes(n, tt)
56
+ # 取該 (layer, tensor) 的第 0 個 expert 的前幾列
57
+ for row in (0, 1, 137):
58
+ byte_off = off + row * nrow
59
+ raw = V.SHARDS[si]
60
+ got = subprocess.run([GGML_DEQ, str(tt), raw, str(byte_off), str(n)],
61
+ capture_output=True, check=True).stdout
62
+ ref = np.frombuffer(got, dtype=np.float32)
63
+ # ggml_deq 讀的是整列(一個 weight 向量),NumPy 這邊用同一段
64
+ # 位元組還原 —— 兩邊必須對齊同一個 offset。
65
+ with open(raw, 'rb') as f:
66
+ f.seek(byte_off)
67
+ mine = V.dequant(f.read(nrow), n, tt)
68
+ assert ref.shape == mine.shape == (n,)
69
+ d = np.abs(ref - mine)
70
+ exact = bool(np.array_equal(ref, mine))
71
+ all_ok &= exact
72
+ rows.append((il, suffix, tt, row, float(d.max()), exact))
73
+ print('%-34s type=%-2d row=%-4d max|NumPy - ggml| = %g %s'
74
+ % (name, tt, row, d.max(), 'EXACT' if exact else 'MISMATCH'))
75
+
76
+ seen = {(r[0], r[1], r[2]) for r in rows}
77
+ if len(seen) != len(TARGETS):
78
+ raise SystemExit('duplicate comparisons: %d rows over %d unique tensors'
79
+ % (len(rows), len(seen)))
80
 
81
  print()
82
  print('%d/%d bit-exact' % (sum(1 for r in rows if r[5]), len(rows)))