@mlx-bun/quantize 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,103 @@
1
+ // Fold plan for the HF (transformers 5) Qwen3.5-family layout: tensors named
2
+ // `model.language_model.*` / `model.visual.*` / top-level `lm_head.weight` /
3
+ // in-repo `mtp.*`. `planQwen35Fold` (rotate.ts) covers the older
4
+ // `language_model.model.*` naming; this plan is its sibling for checkpoints
5
+ // that store RMSNorm gains as γ−1.
6
+ //
7
+ // Gain convention: the stored gain is γ−1 (the loader adds 1 for this family),
8
+ // so the fold uses `stored + 1` and writes every folded norm back as ZEROS —
9
+ // the loader's shift then yields the gain-free norm the rotation needs.
10
+ //
11
+ // Corridors match planQwen35Fold: R1-only (+γ). Readers fold the input dim with
12
+ // the preceding norm's γ, writers fold the output dim; the attention output
13
+ // gate does not commute with a per-head rotation, so there is no R2.
14
+
15
+ import type { FoldOp } from "./rotate";
16
+ import { LM_PREFIX, MTP_PREFIX, VISION_PREFIX } from "./trellis-allocation";
17
+
18
+ export interface Qwen35HfFoldPlan {
19
+ ops: Map<string, FoldOp>;
20
+ /** γ tensors the fold context preloads (stored+1 applied by the executor). */
21
+ gammaNames: string[];
22
+ /** Norm tensors written back as zeros (every folded γ, plus `mtp.norm.weight`). */
23
+ zeroNorms: Set<string>;
24
+ /** The γ folded into `lm_head.weight`: the final norm's. */
25
+ headGamma: string;
26
+ }
27
+
28
+ export interface Qwen35HfFoldSource {
29
+ readonly tensorNames: readonly string[];
30
+ has(name: string): boolean;
31
+ info(name: string): { shape: readonly number[] };
32
+ }
33
+
34
+ /** Plan the fold of every tensor of `source`. The vision merger and the MTP
35
+ * companion are folded when present. Throws when a trunk tensor the plan needs
36
+ * is missing, or when a 2-D trunk tensor is left unplanned. */
37
+ export function planQwen35HfFold(
38
+ source: Qwen35HfFoldSource,
39
+ arch: { numHiddenLayers: number; layerTypes: readonly string[] },
40
+ ): Qwen35HfFoldPlan {
41
+ const ops = new Map<string, FoldOp>();
42
+ const gammaNames: string[] = [];
43
+ const zeroNorms = new Set<string>();
44
+ const addGamma = (name: string): void => { gammaNames.push(name); zeroNorms.add(name); };
45
+ const readers = (block: string, gamma: string, tensors: string[]) => {
46
+ for (const t of tensors) ops.set(`${block}.${t}.weight`, { kind: "input", gamma });
47
+ };
48
+
49
+ const finalNorm = `${LM_PREFIX}norm.weight`;
50
+ addGamma(finalNorm);
51
+ ops.set(`${LM_PREFIX}embed_tokens.weight`, { kind: "input" });
52
+ ops.set("lm_head.weight", { kind: "input", gamma: finalNorm });
53
+
54
+ for (let i = 0; i < arch.numHiddenLayers; i++) {
55
+ const block = `${LM_PREFIX}layers.${i}`;
56
+ const inNorm = `${block}.input_layernorm.weight`;
57
+ const postNorm = `${block}.post_attention_layernorm.weight`;
58
+ addGamma(inNorm);
59
+ addGamma(postNorm);
60
+ const linear = arch.layerTypes[i] === "linear_attention";
61
+ readers(block, inNorm, linear
62
+ ? ["linear_attn.in_proj_qkv", "linear_attn.in_proj_z", "linear_attn.in_proj_b", "linear_attn.in_proj_a"]
63
+ : ["self_attn.q_proj", "self_attn.k_proj", "self_attn.v_proj"]);
64
+ ops.set(`${block}.${linear ? "linear_attn.out_proj" : "self_attn.o_proj"}.weight`, { kind: "output" });
65
+ readers(block, postNorm, ["mlp.gate_proj", "mlp.up_proj"]);
66
+ ops.set(`${block}.mlp.down_proj.weight`, { kind: "output" });
67
+ }
68
+
69
+ if (source.has(`${VISION_PREFIX}merger.linear_fc2.weight`)) {
70
+ ops.set(`${VISION_PREFIX}merger.linear_fc2.weight`, { kind: "output" });
71
+ ops.set(`${VISION_PREFIX}merger.linear_fc2.bias`, { kind: "bias" });
72
+ }
73
+
74
+ if (source.has(`${MTP_PREFIX}fc.weight`)) {
75
+ const embedNorm = `${MTP_PREFIX}pre_fc_norm_embedding.weight`;
76
+ const hiddenNorm = `${MTP_PREFIX}pre_fc_norm_hidden.weight`;
77
+ addGamma(embedNorm);
78
+ addGamma(hiddenNorm);
79
+ zeroNorms.add(`${MTP_PREFIX}norm.weight`);
80
+ ops.set(`${MTP_PREFIX}fc.weight`, { kind: "mtp-fc", gammaEmbed: embedNorm, gammaHidden: hiddenNorm });
81
+ const block = `${MTP_PREFIX}layers.0`;
82
+ const inNorm = `${block}.input_layernorm.weight`;
83
+ const postNorm = `${block}.post_attention_layernorm.weight`;
84
+ addGamma(inNorm);
85
+ addGamma(postNorm);
86
+ readers(block, inNorm, ["self_attn.q_proj", "self_attn.k_proj", "self_attn.v_proj"]);
87
+ ops.set(`${block}.self_attn.o_proj.weight`, { kind: "output" });
88
+ readers(block, postNorm, ["mlp.gate_proj", "mlp.up_proj"]);
89
+ ops.set(`${block}.mlp.down_proj.weight`, { kind: "output" });
90
+ }
91
+
92
+ for (const name of ops.keys())
93
+ if (!source.has(name)) throw new Error(`fold plan: missing source tensor ${name}`);
94
+ for (const name of gammaNames)
95
+ if (!source.has(name)) throw new Error(`fold plan: missing γ tensor ${name}`);
96
+ const unplanned = source.tensorNames.filter(
97
+ (n) => n.endsWith(".weight") && !ops.has(n) && !zeroNorms.has(n) &&
98
+ (n.startsWith(LM_PREFIX) || n.startsWith(MTP_PREFIX)) &&
99
+ source.info(n).shape.length === 2 && !n.includes("_norm.") && !n.endsWith("attn.norm.weight"),
100
+ );
101
+ if (unplanned.length) throw new Error(`fold plan: unplanned 2-D trunk tensors ${unplanned.join(", ")}`);
102
+ return { ops, gammaNames, zeroNorms, headGamma: finalNorm };
103
+ }