txgsync commited on
Commit
0141c28
·
verified ·
1 Parent(s): f1eeeac

Upload verified Maple oQ8e MLX conversion

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ tokenizer.json filter=lfs diff=lfs merge=lfs -text
__pycache__/maple.cpython-311.pyc ADDED
Binary file (57.5 kB). View file
 
added_tokens.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "</think>": 151668,
3
+ "</tool_call>": 151658,
4
+ "</tool_response>": 151666,
5
+ "<think>": 151667,
6
+ "<tool_call>": 151657,
7
+ "<tool_response>": 151665,
8
+ "<|box_end|>": 151649,
9
+ "<|box_start|>": 151648,
10
+ "<|endoftext|>": 151643,
11
+ "<|file_sep|>": 151664,
12
+ "<|fim_middle|>": 151660,
13
+ "<|fim_pad|>": 151662,
14
+ "<|fim_prefix|>": 151659,
15
+ "<|fim_suffix|>": 151661,
16
+ "<|im_end|>": 151645,
17
+ "<|im_start|>": 151644,
18
+ "<|image_pad|>": 151655,
19
+ "<|object_ref_end|>": 151647,
20
+ "<|object_ref_start|>": 151646,
21
+ "<|quad_end|>": 151651,
22
+ "<|quad_start|>": 151650,
23
+ "<|repo_name|>": 151663,
24
+ "<|video_pad|>": 151656,
25
+ "<|vision_end|>": 151653,
26
+ "<|vision_pad|>": 151654,
27
+ "<|vision_start|>": 151652
28
+ }
chat_template.jinja ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- if tools %}
2
+ {{- '<|im_start|>system\n' }}
3
+ {%- if messages[0].role == 'system' %}
4
+ {{- messages[0].content + '\n\n' }}
5
+ {%- endif %}
6
+ {{- '# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>' }}
7
+ {%- for tool in tools %}
8
+ {{- '\n' }}
9
+ {{- tool | tojson }}
10
+ {%- endfor %}
11
+ {{- '\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n' }}
12
+ {%- else %}
13
+ {%- if messages[0].role == 'system' %}
14
+ {{- '<|im_start|>system\n' + messages[0].content + '<|im_end|>\n' }}
15
+ {%- endif %}
16
+ {%- endif %}
17
+
18
+
19
+ {%- for message in messages %}
20
+ {%- if message.content is string %}
21
+ {%- set content = message.content %}
22
+ {%- else %}
23
+ {%- set content = '' %}
24
+ {%- endif %}
25
+
26
+ {%- if message.role == 'user' or (message.role == 'system' and not loop.first) %}
27
+ {{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>\n' }}
28
+
29
+ {%- elif message.role == 'assistant' %}
30
+ {%- set reasoning_content = '' %}
31
+
32
+ {%- if message.reasoning_content is string %}
33
+ {%- set reasoning_content = message.reasoning_content %}
34
+ {%- elif '</think>' in content %}
35
+ {%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
36
+ {%- set content = content.split('</think>')[-1].lstrip('\n') %}
37
+ {%- endif %}
38
+
39
+ {%- if reasoning_content %}
40
+ {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
41
+ {%- else %}
42
+ {{- '<|im_start|>' + message.role + '\n' + content }}
43
+ {%- endif %}
44
+
45
+ {%- if message.tool_calls %}
46
+ {%- for tool_call in message.tool_calls %}
47
+ {%- if (loop.first and content) or not loop.first %}
48
+ {{- '\n' }}
49
+ {%- endif %}
50
+
51
+ {%- if tool_call.function %}
52
+ {%- set tool_call = tool_call.function %}
53
+ {%- endif %}
54
+
55
+ {{- '<tool_call>\n{\"name\": \"' }}
56
+ {{- tool_call.name }}
57
+ {{- '\", \"arguments\": ' }}
58
+
59
+ {%- if tool_call.arguments is string %}
60
+ {{- tool_call.arguments }}
61
+ {%- else %}
62
+ {{- tool_call.arguments | tojson }}
63
+ {%- endif %}
64
+
65
+ {{- '}\n</tool_call>' }}
66
+ {%- endfor %}
67
+ {%- endif %}
68
+
69
+ {{- '<|im_end|>\n' }}
70
+
71
+ {%- elif message.role == 'tool' %}
72
+ {%- if loop.first or messages[loop.index0 - 1].role != 'tool' %}
73
+ {{- '<|im_start|>user' }}
74
+ {%- endif %}
75
+
76
+ {{- '\n<tool_response>\n' }}
77
+ {{- content }}
78
+ {{- '\n</tool_response>' }}
79
+
80
+ {%- if loop.last or messages[loop.index0 + 1].role != 'tool' %}
81
+ {{- '<|im_end|>\n' }}
82
+ {%- endif %}
83
+ {%- endif %}
84
+ {%- endfor %}
85
+
86
+ {%- if add_generation_prompt %}
87
+ {{- '<|im_start|>assistant\n<think>\n' }}
88
+ {%- endif %}
config.json ADDED
@@ -0,0 +1,521 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "MapleForCausalLM"
4
+ ],
5
+ "attention_dropout": 0.0,
6
+ "bos_token_id": 151643,
7
+ "dtype": "bfloat16",
8
+ "embedding_dropout": 0.0,
9
+ "eos_token_id": 151645,
10
+ "head_dim": 128,
11
+ "hidden_act": "silu",
12
+ "hidden_size": 2048,
13
+ "initializer_range": 0.02,
14
+ "intermediate_size": 4096,
15
+ "layer_types": [
16
+ "sliding_attention",
17
+ "sliding_attention",
18
+ "sliding_attention",
19
+ "full_attention",
20
+ "sliding_attention",
21
+ "sliding_attention",
22
+ "sliding_attention",
23
+ "full_attention",
24
+ "sliding_attention",
25
+ "sliding_attention",
26
+ "sliding_attention",
27
+ "full_attention",
28
+ "sliding_attention",
29
+ "sliding_attention",
30
+ "sliding_attention",
31
+ "full_attention",
32
+ "sliding_attention",
33
+ "sliding_attention",
34
+ "sliding_attention",
35
+ "full_attention",
36
+ "sliding_attention",
37
+ "sliding_attention",
38
+ "sliding_attention",
39
+ "full_attention"
40
+ ],
41
+ "max_position_embeddings": 131072,
42
+ "max_window_layers": 24,
43
+ "model_file": "maple.py",
44
+ "model_type": "maple",
45
+ "moe_intermediate_size": 512,
46
+ "moe_router_enable_expert_bias": false,
47
+ "nope_on_global_attention": true,
48
+ "norm_topk_prob": true,
49
+ "num_attention_heads": 16,
50
+ "num_experts": 256,
51
+ "num_experts_per_tok": 8,
52
+ "num_hidden_layers": 24,
53
+ "num_key_value_heads": 4,
54
+ "num_shared_experts": 0,
55
+ "output_dropout": 0.0,
56
+ "output_router_logits": false,
57
+ "pad_token_id": null,
58
+ "partial_rotary_factor": 0.5,
59
+ "preaffine": false,
60
+ "rms_norm_eps": 1e-06,
61
+ "rope_scaling": null,
62
+ "rope_theta": 10000,
63
+ "router_dtype": "fp32",
64
+ "sliding_window": 512,
65
+ "tie_word_embeddings": false,
66
+ "transformers_version": "4.57.1",
67
+ "use_cache": true,
68
+ "use_qk_norm": true,
69
+ "use_rmsnorm": true,
70
+ "vocab_size": 151936,
71
+ "quantization": {
72
+ "group_size": 64,
73
+ "bits": 8,
74
+ "mode": "affine",
75
+ "lm_head": {
76
+ "bits": 8,
77
+ "group_size": 128,
78
+ "mode": "affine"
79
+ },
80
+ "model.layers.0.self_attn.k_proj": {
81
+ "bits": 8,
82
+ "group_size": 128,
83
+ "mode": "affine"
84
+ },
85
+ "model.layers.0.self_attn.q_proj": {
86
+ "bits": 8,
87
+ "group_size": 128,
88
+ "mode": "affine"
89
+ },
90
+ "model.layers.0.self_attn.v_proj": {
91
+ "bits": 8,
92
+ "group_size": 128,
93
+ "mode": "affine"
94
+ },
95
+ "model.layers.1.self_attn.k_proj": {
96
+ "bits": 8,
97
+ "group_size": 128,
98
+ "mode": "affine"
99
+ },
100
+ "model.layers.1.self_attn.q_proj": {
101
+ "bits": 8,
102
+ "group_size": 128,
103
+ "mode": "affine"
104
+ },
105
+ "model.layers.1.self_attn.v_proj": {
106
+ "bits": 8,
107
+ "group_size": 128,
108
+ "mode": "affine"
109
+ },
110
+ "model.word_embeddings": {
111
+ "bits": 8,
112
+ "group_size": 128,
113
+ "mode": "affine"
114
+ },
115
+ "model.layers.2.self_attn.k_proj": {
116
+ "bits": 8,
117
+ "group_size": 128,
118
+ "mode": "affine"
119
+ },
120
+ "model.layers.2.self_attn.q_proj": {
121
+ "bits": 8,
122
+ "group_size": 128,
123
+ "mode": "affine"
124
+ },
125
+ "model.layers.2.self_attn.v_proj": {
126
+ "bits": 8,
127
+ "group_size": 128,
128
+ "mode": "affine"
129
+ },
130
+ "model.layers.3.self_attn.k_proj": {
131
+ "bits": 8,
132
+ "group_size": 128,
133
+ "mode": "affine"
134
+ },
135
+ "model.layers.3.self_attn.q_proj": {
136
+ "bits": 8,
137
+ "group_size": 128,
138
+ "mode": "affine"
139
+ },
140
+ "model.layers.3.self_attn.v_proj": {
141
+ "bits": 8,
142
+ "group_size": 128,
143
+ "mode": "affine"
144
+ },
145
+ "model.layers.4.self_attn.k_proj": {
146
+ "bits": 8,
147
+ "group_size": 128,
148
+ "mode": "affine"
149
+ },
150
+ "model.layers.4.self_attn.q_proj": {
151
+ "bits": 8,
152
+ "group_size": 128,
153
+ "mode": "affine"
154
+ },
155
+ "model.layers.4.self_attn.v_proj": {
156
+ "bits": 8,
157
+ "group_size": 128,
158
+ "mode": "affine"
159
+ },
160
+ "model.layers.22.self_attn.k_proj": {
161
+ "bits": 8,
162
+ "group_size": 128,
163
+ "mode": "affine"
164
+ },
165
+ "model.layers.22.self_attn.q_proj": {
166
+ "bits": 8,
167
+ "group_size": 128,
168
+ "mode": "affine"
169
+ },
170
+ "model.layers.22.self_attn.v_proj": {
171
+ "bits": 8,
172
+ "group_size": 128,
173
+ "mode": "affine"
174
+ },
175
+ "model.layers.0.self_attn.qkv_proj": {
176
+ "bits": 8,
177
+ "group_size": 128,
178
+ "mode": "affine"
179
+ },
180
+ "model.layers.1.self_attn.qkv_proj": {
181
+ "bits": 8,
182
+ "group_size": 128,
183
+ "mode": "affine"
184
+ },
185
+ "model.layers.2.self_attn.qkv_proj": {
186
+ "bits": 8,
187
+ "group_size": 128,
188
+ "mode": "affine"
189
+ },
190
+ "model.layers.3.self_attn.qkv_proj": {
191
+ "bits": 8,
192
+ "group_size": 128,
193
+ "mode": "affine"
194
+ },
195
+ "model.layers.4.self_attn.qkv_proj": {
196
+ "bits": 8,
197
+ "group_size": 128,
198
+ "mode": "affine"
199
+ },
200
+ "model.layers.5.self_attn.qkv_proj": {
201
+ "group_size": 64,
202
+ "bits": 8,
203
+ "mode": "affine"
204
+ },
205
+ "model.layers.6.self_attn.qkv_proj": {
206
+ "group_size": 64,
207
+ "bits": 8,
208
+ "mode": "affine"
209
+ },
210
+ "model.layers.7.self_attn.qkv_proj": {
211
+ "group_size": 64,
212
+ "bits": 8,
213
+ "mode": "affine"
214
+ },
215
+ "model.layers.8.self_attn.qkv_proj": {
216
+ "group_size": 64,
217
+ "bits": 8,
218
+ "mode": "affine"
219
+ },
220
+ "model.layers.9.self_attn.qkv_proj": {
221
+ "group_size": 64,
222
+ "bits": 8,
223
+ "mode": "affine"
224
+ },
225
+ "model.layers.10.self_attn.qkv_proj": {
226
+ "group_size": 64,
227
+ "bits": 8,
228
+ "mode": "affine"
229
+ },
230
+ "model.layers.11.self_attn.qkv_proj": {
231
+ "group_size": 64,
232
+ "bits": 8,
233
+ "mode": "affine"
234
+ },
235
+ "model.layers.12.self_attn.qkv_proj": {
236
+ "group_size": 64,
237
+ "bits": 8,
238
+ "mode": "affine"
239
+ },
240
+ "model.layers.13.self_attn.qkv_proj": {
241
+ "group_size": 64,
242
+ "bits": 8,
243
+ "mode": "affine"
244
+ },
245
+ "model.layers.14.self_attn.qkv_proj": {
246
+ "group_size": 64,
247
+ "bits": 8,
248
+ "mode": "affine"
249
+ },
250
+ "model.layers.15.self_attn.qkv_proj": {
251
+ "group_size": 64,
252
+ "bits": 8,
253
+ "mode": "affine"
254
+ },
255
+ "model.layers.16.self_attn.qkv_proj": {
256
+ "group_size": 64,
257
+ "bits": 8,
258
+ "mode": "affine"
259
+ },
260
+ "model.layers.17.self_attn.qkv_proj": {
261
+ "group_size": 64,
262
+ "bits": 8,
263
+ "mode": "affine"
264
+ },
265
+ "model.layers.18.self_attn.qkv_proj": {
266
+ "group_size": 64,
267
+ "bits": 8,
268
+ "mode": "affine"
269
+ },
270
+ "model.layers.19.self_attn.qkv_proj": {
271
+ "group_size": 64,
272
+ "bits": 8,
273
+ "mode": "affine"
274
+ },
275
+ "model.layers.20.self_attn.qkv_proj": {
276
+ "group_size": 64,
277
+ "bits": 8,
278
+ "mode": "affine"
279
+ },
280
+ "model.layers.21.self_attn.qkv_proj": {
281
+ "group_size": 64,
282
+ "bits": 8,
283
+ "mode": "affine"
284
+ },
285
+ "model.layers.22.self_attn.qkv_proj": {
286
+ "bits": 8,
287
+ "group_size": 128,
288
+ "mode": "affine"
289
+ },
290
+ "model.layers.23.self_attn.qkv_proj": {
291
+ "group_size": 64,
292
+ "bits": 8,
293
+ "mode": "affine"
294
+ }
295
+ },
296
+ "quantization_config": {
297
+ "group_size": 64,
298
+ "bits": 8,
299
+ "mode": "affine",
300
+ "lm_head": {
301
+ "bits": 8,
302
+ "group_size": 128,
303
+ "mode": "affine"
304
+ },
305
+ "model.layers.0.self_attn.k_proj": {
306
+ "bits": 8,
307
+ "group_size": 128,
308
+ "mode": "affine"
309
+ },
310
+ "model.layers.0.self_attn.q_proj": {
311
+ "bits": 8,
312
+ "group_size": 128,
313
+ "mode": "affine"
314
+ },
315
+ "model.layers.0.self_attn.v_proj": {
316
+ "bits": 8,
317
+ "group_size": 128,
318
+ "mode": "affine"
319
+ },
320
+ "model.layers.1.self_attn.k_proj": {
321
+ "bits": 8,
322
+ "group_size": 128,
323
+ "mode": "affine"
324
+ },
325
+ "model.layers.1.self_attn.q_proj": {
326
+ "bits": 8,
327
+ "group_size": 128,
328
+ "mode": "affine"
329
+ },
330
+ "model.layers.1.self_attn.v_proj": {
331
+ "bits": 8,
332
+ "group_size": 128,
333
+ "mode": "affine"
334
+ },
335
+ "model.word_embeddings": {
336
+ "bits": 8,
337
+ "group_size": 128,
338
+ "mode": "affine"
339
+ },
340
+ "model.layers.2.self_attn.k_proj": {
341
+ "bits": 8,
342
+ "group_size": 128,
343
+ "mode": "affine"
344
+ },
345
+ "model.layers.2.self_attn.q_proj": {
346
+ "bits": 8,
347
+ "group_size": 128,
348
+ "mode": "affine"
349
+ },
350
+ "model.layers.2.self_attn.v_proj": {
351
+ "bits": 8,
352
+ "group_size": 128,
353
+ "mode": "affine"
354
+ },
355
+ "model.layers.3.self_attn.k_proj": {
356
+ "bits": 8,
357
+ "group_size": 128,
358
+ "mode": "affine"
359
+ },
360
+ "model.layers.3.self_attn.q_proj": {
361
+ "bits": 8,
362
+ "group_size": 128,
363
+ "mode": "affine"
364
+ },
365
+ "model.layers.3.self_attn.v_proj": {
366
+ "bits": 8,
367
+ "group_size": 128,
368
+ "mode": "affine"
369
+ },
370
+ "model.layers.4.self_attn.k_proj": {
371
+ "bits": 8,
372
+ "group_size": 128,
373
+ "mode": "affine"
374
+ },
375
+ "model.layers.4.self_attn.q_proj": {
376
+ "bits": 8,
377
+ "group_size": 128,
378
+ "mode": "affine"
379
+ },
380
+ "model.layers.4.self_attn.v_proj": {
381
+ "bits": 8,
382
+ "group_size": 128,
383
+ "mode": "affine"
384
+ },
385
+ "model.layers.22.self_attn.k_proj": {
386
+ "bits": 8,
387
+ "group_size": 128,
388
+ "mode": "affine"
389
+ },
390
+ "model.layers.22.self_attn.q_proj": {
391
+ "bits": 8,
392
+ "group_size": 128,
393
+ "mode": "affine"
394
+ },
395
+ "model.layers.22.self_attn.v_proj": {
396
+ "bits": 8,
397
+ "group_size": 128,
398
+ "mode": "affine"
399
+ },
400
+ "model.layers.0.self_attn.qkv_proj": {
401
+ "bits": 8,
402
+ "group_size": 128,
403
+ "mode": "affine"
404
+ },
405
+ "model.layers.1.self_attn.qkv_proj": {
406
+ "bits": 8,
407
+ "group_size": 128,
408
+ "mode": "affine"
409
+ },
410
+ "model.layers.2.self_attn.qkv_proj": {
411
+ "bits": 8,
412
+ "group_size": 128,
413
+ "mode": "affine"
414
+ },
415
+ "model.layers.3.self_attn.qkv_proj": {
416
+ "bits": 8,
417
+ "group_size": 128,
418
+ "mode": "affine"
419
+ },
420
+ "model.layers.4.self_attn.qkv_proj": {
421
+ "bits": 8,
422
+ "group_size": 128,
423
+ "mode": "affine"
424
+ },
425
+ "model.layers.5.self_attn.qkv_proj": {
426
+ "group_size": 64,
427
+ "bits": 8,
428
+ "mode": "affine"
429
+ },
430
+ "model.layers.6.self_attn.qkv_proj": {
431
+ "group_size": 64,
432
+ "bits": 8,
433
+ "mode": "affine"
434
+ },
435
+ "model.layers.7.self_attn.qkv_proj": {
436
+ "group_size": 64,
437
+ "bits": 8,
438
+ "mode": "affine"
439
+ },
440
+ "model.layers.8.self_attn.qkv_proj": {
441
+ "group_size": 64,
442
+ "bits": 8,
443
+ "mode": "affine"
444
+ },
445
+ "model.layers.9.self_attn.qkv_proj": {
446
+ "group_size": 64,
447
+ "bits": 8,
448
+ "mode": "affine"
449
+ },
450
+ "model.layers.10.self_attn.qkv_proj": {
451
+ "group_size": 64,
452
+ "bits": 8,
453
+ "mode": "affine"
454
+ },
455
+ "model.layers.11.self_attn.qkv_proj": {
456
+ "group_size": 64,
457
+ "bits": 8,
458
+ "mode": "affine"
459
+ },
460
+ "model.layers.12.self_attn.qkv_proj": {
461
+ "group_size": 64,
462
+ "bits": 8,
463
+ "mode": "affine"
464
+ },
465
+ "model.layers.13.self_attn.qkv_proj": {
466
+ "group_size": 64,
467
+ "bits": 8,
468
+ "mode": "affine"
469
+ },
470
+ "model.layers.14.self_attn.qkv_proj": {
471
+ "group_size": 64,
472
+ "bits": 8,
473
+ "mode": "affine"
474
+ },
475
+ "model.layers.15.self_attn.qkv_proj": {
476
+ "group_size": 64,
477
+ "bits": 8,
478
+ "mode": "affine"
479
+ },
480
+ "model.layers.16.self_attn.qkv_proj": {
481
+ "group_size": 64,
482
+ "bits": 8,
483
+ "mode": "affine"
484
+ },
485
+ "model.layers.17.self_attn.qkv_proj": {
486
+ "group_size": 64,
487
+ "bits": 8,
488
+ "mode": "affine"
489
+ },
490
+ "model.layers.18.self_attn.qkv_proj": {
491
+ "group_size": 64,
492
+ "bits": 8,
493
+ "mode": "affine"
494
+ },
495
+ "model.layers.19.self_attn.qkv_proj": {
496
+ "group_size": 64,
497
+ "bits": 8,
498
+ "mode": "affine"
499
+ },
500
+ "model.layers.20.self_attn.qkv_proj": {
501
+ "group_size": 64,
502
+ "bits": 8,
503
+ "mode": "affine"
504
+ },
505
+ "model.layers.21.self_attn.qkv_proj": {
506
+ "group_size": 64,
507
+ "bits": 8,
508
+ "mode": "affine"
509
+ },
510
+ "model.layers.22.self_attn.qkv_proj": {
511
+ "bits": 8,
512
+ "group_size": 128,
513
+ "mode": "affine"
514
+ },
515
+ "model.layers.23.self_attn.qkv_proj": {
516
+ "group_size": 64,
517
+ "bits": 8,
518
+ "mode": "affine"
519
+ }
520
+ }
521
+ }
maple.py ADDED
@@ -0,0 +1,1095 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright © 2026 DeepGrove AI.
2
+
3
+ from dataclasses import dataclass
4
+ from functools import partial
5
+ from typing import Any, List, Optional
6
+
7
+ import mlx.core as mx
8
+ import mlx.nn as nn
9
+
10
+ # Absolute imports so this file also works standalone when shipped inside a
11
+ # checkpoint and loaded via the config's `model_file` (trust_remote_code).
12
+ from mlx_lm.models.activations import swiglu
13
+ from mlx_lm.models.base import (
14
+ BaseModelArgs,
15
+ create_attention_mask,
16
+ scaled_dot_product_attention,
17
+ )
18
+ from mlx_lm.models.cache import KVCache, RotatingKVCache
19
+ from mlx_lm.models.rope_utils import initialize_rope
20
+ from mlx_lm.models.switch_layers import SwitchLinear
21
+
22
+ # SwiGLU clamp for the MoE experts only (the dense MapleMLP is unclamped);
23
+ # part of the trained forward pass, not an optional guard.
24
+ MLP_CLAMP = 7.0
25
+
26
+
27
+ @partial(mx.compile, shapeless=True)
28
+ def clamped_swiglu(gate, x):
29
+ # Python floats, not 0-d arrays, so bf16 activations stay bf16.
30
+ return nn.silu(mx.minimum(gate, MLP_CLAMP)) * mx.clip(x, -MLP_CLAMP, MLP_CLAMP)
31
+
32
+
33
+ def _matches(fast, reference, tol=2e-2):
34
+ """One-time self-check for a hand-written Metal kernel.
35
+
36
+ Every fast path below has a portable equivalent, and each is used only
37
+ after its outputs have been compared against that equivalent once, on the
38
+ live weights. This file ships inside checkpoints and runs on whatever mlx
39
+ and GPU the user has, so a kernel that fails to compile, silently mismatches
40
+ the config it was templated for, or drifts from a future mlx must degrade to
41
+ the portable path rather than corrupt the token stream.
42
+
43
+ Both callables return a tuple of arrays. The kernels stay in bounds for any
44
+ config (loop counts are integer-divided from the templated dims), so a
45
+ config they cannot handle shows up here as wrong values, not as a fault.
46
+ """
47
+ try:
48
+ got, want = fast(), reference()
49
+ mx.eval(got, want)
50
+ except Exception:
51
+ return False
52
+ return len(got) == len(want) and all(
53
+ g.shape == w.shape
54
+ and bool(
55
+ mx.allclose(g.astype(mx.float32), w.astype(mx.float32), rtol=tol, atol=tol)
56
+ )
57
+ for g, w in zip(got, want)
58
+ )
59
+
60
+
61
+ class MapleRMSNorm(nn.Module):
62
+ """RMSNorm with the weight multiply in float32.
63
+
64
+ The reference rounds only the finished product; mx.fast.rms_norm rounds
65
+ the normalized activation first (~1% per element). Float32 inputs to the
66
+ same kernel reproduce the reference bit-for-bit.
67
+ """
68
+
69
+ def __init__(self, dims: int, eps: float = 1e-6):
70
+ super().__init__()
71
+ self.weight = mx.ones((dims,))
72
+ self.eps = eps
73
+
74
+ def __call__(self, x: mx.array) -> mx.array:
75
+ return mx.fast.rms_norm(
76
+ x.astype(mx.float32), self.weight.astype(mx.float32), self.eps
77
+ ).astype(x.dtype)
78
+
79
+
80
+ def _make_add_rms_norm_kernel(eps):
81
+ """Residual add + RMSNorm in ONE dispatch for single-token decode.
82
+
83
+ Emits both h = x + r (the residual stream, rounded once like a bf16 add)
84
+ and hn = rmsnorm(h) with the weight multiply in fp32 (reference
85
+ semantics, identical to MapleRMSNorm). Folding the add into the norm and
86
+ skipping the astype round-trips replaces ~4 dispatches with 1, and the
87
+ decode step is bounded by its serial dispatch chain, not by this math.
88
+ """
89
+ source = """
90
+ uint tid = thread_position_in_threadgroup.x;
91
+ constexpr uint N = DIM;
92
+ constexpr uint PT = N / 256u;
93
+ float hb[PT];
94
+ float ss = 0.0f;
95
+ for (uint i = 0; i < PT; ++i) {
96
+ uint j = tid * PT + i;
97
+ float v = (float)x[j] + (float)r[j];
98
+ T_ vb = (T_)v; // one rounding, same as a bf16 add
99
+ h_out[j] = vb;
100
+ hb[i] = (float)vb; // norm sees the rounded stream
101
+ ss += hb[i] * hb[i];
102
+ }
103
+ ss = simd_sum(ss);
104
+ threadgroup float sums[8];
105
+ uint sg = tid / 32u;
106
+ uint lane = tid % 32u;
107
+ if (lane == 0u) sums[sg] = ss;
108
+ threadgroup_barrier(mem_flags::mem_threadgroup);
109
+ float tot = 0.0f;
110
+ for (uint i = 0; i < 8u; ++i) tot += sums[i];
111
+ float scale = metal::rsqrt(tot / (float)N + EPS_);
112
+ for (uint i = 0; i < PT; ++i) {
113
+ uint j = tid * PT + i;
114
+ hn_out[j] = (T_)(hb[i] * scale * (float)w[j]);
115
+ }
116
+ """.replace("EPS_", f"{eps:.10e}f")
117
+ tag = f"{eps:.3e}".replace(".", "_").replace("-", "m").replace("+", "p")
118
+ return mx.fast.metal_kernel(
119
+ name=f"maple_add_rms_norm_{tag}",
120
+ input_names=["x", "r", "w"],
121
+ output_names=["h_out", "hn_out"],
122
+ source=source,
123
+ )
124
+
125
+
126
+ _add_rms_kernels = {}
127
+
128
+
129
+ def _add_rms_norm(h, r, w, eps):
130
+ kernel = _add_rms_kernels.get(eps)
131
+ if kernel is None:
132
+ kernel = _add_rms_kernels[eps] = _make_add_rms_norm_kernel(eps)
133
+ return kernel(
134
+ inputs=[h.reshape(-1), r.reshape(-1), w],
135
+ template=[("T_", h.dtype), ("DIM", h.shape[-1])],
136
+ grid=(256, 1, 1),
137
+ threadgroup=(256, 1, 1),
138
+ output_shapes=[h.shape, h.shape],
139
+ output_dtypes=[h.dtype, h.dtype],
140
+ )
141
+
142
+
143
+ def _add_rms_norm_ok(dim, dtype, w, eps):
144
+ x = mx.random.normal((1, 1, dim), key=mx.random.key(0)).astype(dtype)
145
+ r = mx.random.normal((1, 1, dim), key=mx.random.key(1)).astype(dtype)
146
+ return _matches(
147
+ lambda: _add_rms_norm(x, r, w, eps),
148
+ lambda: (
149
+ x + r,
150
+ mx.fast.rms_norm(
151
+ (x + r).astype(mx.float32), w.astype(mx.float32), eps
152
+ ).astype(dtype),
153
+ ),
154
+ )
155
+
156
+
157
+ # Inlined rather than imported from switch_layers: those helpers are private
158
+ # (underscore-prefixed), and this file must keep loading against whatever
159
+ # mlx-lm a user has installed when it ships inside a checkpoint.
160
+ def _gather_sort(x, indices):
161
+ *_, M = indices.shape
162
+ indices = indices.flatten()
163
+ order = mx.argsort(indices)
164
+ inv_order = mx.argsort(order)
165
+ return x.flatten(0, -3)[order // M], indices[order], inv_order
166
+
167
+
168
+ def _scatter_unsort(x, inv_order, shape=None):
169
+ x = x[inv_order]
170
+ if shape is not None:
171
+ x = mx.unflatten(x, 0, shape)
172
+ return x
173
+
174
+
175
+ @dataclass
176
+ class ModelArgs(BaseModelArgs):
177
+ model_type: str = "maple"
178
+ hidden_size: int = 2048
179
+ intermediate_size: int = 5120
180
+ moe_intermediate_size: int = 512
181
+ num_hidden_layers: int = 24
182
+ num_attention_heads: int = 16
183
+ num_key_value_heads: int = 4
184
+ head_dim: int = 128
185
+ num_experts: int = 256
186
+ num_experts_per_tok: int = 8
187
+ first_k_dense_replace: int = 0
188
+ rms_norm_eps: float = 1e-6
189
+ rope_theta: float = 10000.0
190
+ rope_scaling: Optional[dict] = None
191
+ partial_rotary_factor: float = 0.5
192
+ max_position_embeddings: int = 140000
193
+ vocab_size: int = 151936
194
+ sliding_window: int = 512
195
+ layer_types: Optional[List[str]] = None
196
+ use_qk_norm: bool = True
197
+ use_bias: bool = False
198
+ tie_word_embeddings: bool = False
199
+ # FlashHead metadata written by `mlx_lm.ternary --flash-head`. The exact
200
+ # lm_head is the default; opt in to the approximate fast head with
201
+ # mlx_lm.load(..., model_config={"use_flash_head": True}).
202
+ flash_head: Optional[dict] = None
203
+ use_flash_head: bool = False
204
+ # Populated from the checkpoint's config; sanitize() reads group_size from
205
+ # it to expand row-scale (`row_alpha`) ternary tensors.
206
+ quantization: Optional[dict] = None
207
+
208
+ def __post_init__(self):
209
+ # Single source of truth for per-layer attention types: attention
210
+ # (RoPE/NoPE), masks, and caches all read this resolved list.
211
+ if not self.layer_types:
212
+ self.layer_types = ["full_attention"] * self.num_hidden_layers
213
+
214
+
215
+ def _make_qk_norm_rope_kernel():
216
+ """Fused per-head RMSNorm + partial RoPE for single-token decode.
217
+
218
+ One dispatch replaces q_norm, k_norm and two rope calls. One simdgroup per
219
+ head: normalize head_dim values, scale by the head's norm weight, and
220
+ rotate the first ROPE_DIM dims (non-traditional pairing i, i+R/2) at the
221
+ given position. NoPE layers pass ROPE_DIM=0.
222
+ """
223
+ source = """
224
+ uint head = thread_position_in_grid.y;
225
+ uint lane = thread_position_in_grid.x;
226
+
227
+ constexpr int per_lane = HEAD_DIM / 32;
228
+ const device T_* xh = x + head * HEAD_DIM;
229
+ const device T_* wh = w + head * HEAD_DIM;
230
+ device T_* oh = out + head * HEAD_DIM;
231
+
232
+ float ss = 0.0f;
233
+ for (int i = 0; i < per_lane; ++i) {
234
+ float v = (float)xh[lane * per_lane + i];
235
+ ss += v * v;
236
+ }
237
+ ss = simd_sum(ss);
238
+ float pos = pos_eps[0];
239
+ float eps = pos_eps[1];
240
+ float scale = metal::rsqrt(ss / HEAD_DIM + eps);
241
+
242
+ for (int i = 0; i < per_lane; ++i) {
243
+ int j = lane * per_lane + i;
244
+ float v = (float)xh[j] * scale * (float)wh[j];
245
+ if (ROPE_DIM > 0 && j < ROPE_DIM) {
246
+ constexpr int rhalf = ROPE_DIM > 0 ? ROPE_DIM / 2 : 1;
247
+ int p = j < rhalf ? j : j - rhalf;
248
+ float theta = pos * inv_freq[p];
249
+ float c = metal::cos(theta);
250
+ float s = metal::sin(theta);
251
+ int j2 = j < rhalf ? j + rhalf : j - rhalf;
252
+ float u = (float)xh[j2] * scale * (float)wh[j2];
253
+ v = j < rhalf ? (v * c - u * s) : (v * c + u * s);
254
+ }
255
+ oh[j] = (T_)v;
256
+ }
257
+ """
258
+ return mx.fast.metal_kernel(
259
+ name="maple_qk_norm_rope",
260
+ input_names=["x", "w", "inv_freq", "pos_eps"],
261
+ output_names=["out"],
262
+ source=source,
263
+ )
264
+
265
+
266
+ _qk_norm_rope_kernel = _make_qk_norm_rope_kernel()
267
+
268
+
269
+ class MapleAttention(nn.Module):
270
+ def __init__(self, args: ModelArgs, layer_idx: int):
271
+ super().__init__()
272
+ self.num_attention_heads = args.num_attention_heads
273
+ self.num_key_value_heads = args.num_key_value_heads
274
+ self.head_dim = args.head_dim or args.hidden_size // args.num_attention_heads
275
+ self.scale = self.head_dim**-0.5
276
+ self.use_qk_norm = args.use_qk_norm
277
+
278
+ # q/k/v are stored fused (one matmul per step); sanitize() concatenates
279
+ # the checkpoint's split projections.
280
+ self.qkv_proj = nn.Linear(
281
+ args.hidden_size,
282
+ (args.num_attention_heads + 2 * args.num_key_value_heads) * self.head_dim,
283
+ bias=args.use_bias,
284
+ )
285
+ self.o_proj = nn.Linear(
286
+ args.num_attention_heads * self.head_dim,
287
+ args.hidden_size,
288
+ bias=args.use_bias,
289
+ )
290
+
291
+ if args.use_qk_norm:
292
+ self.q_norm = MapleRMSNorm(self.head_dim, eps=args.rms_norm_eps)
293
+ self.k_norm = MapleRMSNorm(self.head_dim, eps=args.rms_norm_eps)
294
+ self._eps = args.rms_norm_eps
295
+ self._rope_base = args.rope_theta
296
+ self._qk_w = None
297
+ self._inv_freq = None
298
+ self._fused_qk = None # None = unprobed, then True/False
299
+
300
+ # Maple applies RoPE only on sliding-window layers; full-attention
301
+ # layers use no positional encoding (NoPE).
302
+ self.use_rope = args.layer_types[layer_idx] == "sliding_attention"
303
+ if self.use_rope:
304
+ rope_dim = int(self.head_dim * args.partial_rotary_factor)
305
+ self.rope = initialize_rope(
306
+ rope_dim,
307
+ args.rope_theta,
308
+ traditional=False,
309
+ scaling_config=args.rope_scaling,
310
+ max_position_embeddings=args.max_position_embeddings,
311
+ )
312
+
313
+ def _qk_fused(self, qk, offset):
314
+ """Both norms and both rope applications in one dispatch."""
315
+ if self._qk_w is None:
316
+ n_q = self.num_attention_heads
317
+ n_kv = self.num_key_value_heads
318
+ self._qk_w = mx.contiguous(
319
+ mx.concatenate(
320
+ [
321
+ mx.broadcast_to(self.q_norm.weight[None], (n_q, self.head_dim)),
322
+ mx.broadcast_to(
323
+ self.k_norm.weight[None], (n_kv, self.head_dim)
324
+ ),
325
+ ]
326
+ )
327
+ )
328
+ if self.use_rope:
329
+ half = self.rope.dims // 2
330
+ self._inv_freq = self._rope_base ** (
331
+ -mx.arange(half, dtype=mx.float32) / half
332
+ )
333
+ else:
334
+ self._inv_freq = mx.ones((1,), dtype=mx.float32)
335
+ mx.eval(self._qk_w, self._inv_freq)
336
+
337
+ # cache.offset is a Python int for a plain cache but an mx.array for
338
+ # the batched caches; coerce so the pos/eps pair is always uniform.
339
+ pos_eps = mx.array([float(offset), self._eps], dtype=mx.float32)
340
+ return _qk_norm_rope_kernel(
341
+ inputs=[qk, self._qk_w, self._inv_freq, pos_eps],
342
+ template=[
343
+ ("T_", qk.dtype),
344
+ ("HEAD_DIM", self.head_dim),
345
+ ("ROPE_DIM", self.rope.dims if self.use_rope else 0),
346
+ ],
347
+ grid=(32, qk.shape[0], 1),
348
+ threadgroup=(32, 1, 1),
349
+ output_shapes=[qk.shape],
350
+ output_dtypes=[qk.dtype],
351
+ )[0]
352
+
353
+ def _qk_reference(self, qk, offset):
354
+ """The same result from stock ops: fallback, and the yardstick the
355
+ fused kernel is checked against."""
356
+ n_q = self.num_attention_heads
357
+ q = self.q_norm(qk[None, :n_q, None, :])
358
+ k = self.k_norm(qk[None, n_q:, None, :])
359
+ if self.use_rope:
360
+ q = self.rope(q, offset=offset)
361
+ k = self.rope(k, offset=offset)
362
+ return mx.concatenate([q, k], axis=1).reshape(qk.shape)
363
+
364
+ def __call__(
365
+ self,
366
+ x: mx.array,
367
+ mask: Optional[mx.array] = None,
368
+ cache: Optional[Any] = None,
369
+ ) -> mx.array:
370
+ B, L, _ = x.shape
371
+
372
+ qkv = self.qkv_proj(x)
373
+
374
+ if B == 1 and L == 1 and self.use_qk_norm:
375
+ n_q = self.num_attention_heads
376
+ n_kv = self.num_key_value_heads
377
+ qk_size = (n_q + n_kv) * self.head_dim
378
+ qk = qkv.reshape(-1)[:qk_size].reshape(n_q + n_kv, self.head_dim)
379
+ if self._fused_qk is None:
380
+ # A nonzero position, so a broken rotation cannot pass.
381
+ self._fused_qk = _matches(
382
+ lambda: (self._qk_fused(qk, 7),),
383
+ lambda: (self._qk_reference(qk, 7),),
384
+ )
385
+ offset = cache.offset if cache is not None else 0
386
+ out = (self._qk_fused if self._fused_qk else self._qk_reference)(qk, offset)
387
+ queries = out[:n_q].reshape(1, n_q, 1, self.head_dim)
388
+ keys = out[n_q:].reshape(1, n_kv, 1, self.head_dim)
389
+ values = qkv.reshape(-1)[qk_size:].reshape(1, n_kv, 1, self.head_dim)
390
+ else:
391
+ q_size = self.num_attention_heads * self.head_dim
392
+ kv_size = self.num_key_value_heads * self.head_dim
393
+ q, k, v = mx.split(qkv, [q_size, q_size + kv_size], axis=-1)
394
+
395
+ queries = q.reshape(B, L, self.num_attention_heads, self.head_dim)
396
+ keys = k.reshape(B, L, self.num_key_value_heads, self.head_dim)
397
+ values = v.reshape(B, L, self.num_key_value_heads, self.head_dim)
398
+
399
+ if self.use_qk_norm:
400
+ queries = self.q_norm(queries)
401
+ keys = self.k_norm(keys)
402
+
403
+ queries = queries.transpose(0, 2, 1, 3)
404
+ keys = keys.transpose(0, 2, 1, 3)
405
+ values = values.transpose(0, 2, 1, 3)
406
+
407
+ if self.use_rope:
408
+ offset = cache.offset if cache is not None else 0
409
+ queries = self.rope(queries, offset=offset)
410
+ keys = self.rope(keys, offset=offset)
411
+
412
+ if cache is not None:
413
+ keys, values = cache.update_and_fetch(keys, values)
414
+
415
+ output = scaled_dot_product_attention(
416
+ queries, keys, values, cache=cache, scale=self.scale, mask=mask
417
+ )
418
+
419
+ output = output.transpose(0, 2, 1, 3).reshape(B, L, -1)
420
+ return self.o_proj(output)
421
+
422
+
423
+ class MapleMLP(nn.Module):
424
+ def __init__(self, args: ModelArgs, intermediate_size: Optional[int] = None):
425
+ super().__init__()
426
+ intermediate_size = intermediate_size or args.intermediate_size
427
+ self.gate_proj = nn.Linear(
428
+ args.hidden_size, intermediate_size, bias=args.use_bias
429
+ )
430
+ self.up_proj = nn.Linear(
431
+ args.hidden_size, intermediate_size, bias=args.use_bias
432
+ )
433
+ self.down_proj = nn.Linear(
434
+ intermediate_size, args.hidden_size, bias=args.use_bias
435
+ )
436
+
437
+ def __call__(self, x) -> mx.array:
438
+ # Dense / shared-expert MLP: no clamp; only the MoE experts clamp.
439
+ # Unused at first_k_dense_replace=0 with no shared experts, but keep
440
+ # it faithful.
441
+ return self.down_proj(swiglu(self.gate_proj(x), self.up_proj(x)))
442
+
443
+
444
+ @mx.compile
445
+ def group_expert_select(gates, top_k):
446
+ # Maple routes with a plain softmax over all experts followed by top-k
447
+ # selection and renormalization, computed in float32.
448
+ scores = mx.softmax(gates.astype(mx.float32), axis=-1)
449
+ inds = mx.argpartition(scores, kth=-top_k, axis=-1)[..., -top_k:]
450
+ scores = mx.take_along_axis(scores, inds, axis=-1)
451
+ scores = scores / (scores.sum(axis=-1, keepdims=True) + 1e-20)
452
+ return inds, scores
453
+
454
+
455
+ def _make_fused_router_kernel():
456
+ """Router gemv + softmax + top-8 + renormalize in ONE dispatch (+18%).
457
+
458
+ Replaces ~6 kernels per layer. NE/32 threadgroups each compute 32 logits,
459
+ keep them in float32 (`router_dtype: fp32`), and publish through an
460
+ atomic-float scratch (plain device stores are not reliably visible across
461
+ threadgroups on Apple GPUs); the last threadgroup to arrive does the
462
+ softmax + top-8 + renorm.
463
+
464
+ `ctr_in` is a persistent arrival counter, not an input: every dispatch
465
+ must see it at zero, so the electing threadgroup resets it on its way out
466
+ and each MapleGate keeps its own. Election on a stale counter would read
467
+ unwritten scratch, so nothing else may share the buffer.
468
+ """
469
+ source = """
470
+ constexpr uint NE = NEXP;
471
+ constexpr uint D = DIM;
472
+ constexpr uint NTG = NE / 32u;
473
+ constexpr uint TM = 4u;
474
+ constexpr uint TN = 4u;
475
+ constexpr uint BLOCKN = 32u * TN;
476
+ constexpr uint NITER = D / BLOCKN;
477
+
478
+ uint tid = thread_position_in_threadgroup.x;
479
+ uint tgid = threadgroup_position_in_grid.x;
480
+ uint n_threads = 256u;
481
+ uint sg_id = tid / 32u;
482
+ uint lane = tid % 32u;
483
+ uint n_sg = n_threads / 32u;
484
+
485
+ uint row0 = tgid * (n_sg * TM) + sg_id * TM;
486
+ float result[TM] = {0.0f, 0.0f, 0.0f, 0.0f};
487
+ uint bn = lane * TN;
488
+ for (uint i = 0u; i < NITER; ++i) {
489
+ float v[TN];
490
+ for (uint tn = 0u; tn < TN; ++tn) v[tn] = float(x[bn + tn]);
491
+ for (uint tm = 0u; tm < TM; ++tm) {
492
+ const device T_* wrow = w + (ulong)(row0 + tm) * D;
493
+ T_ inter[TN];
494
+ for (uint tn = 0u; tn < TN; ++tn) inter[tn] = wrow[bn + tn];
495
+ for (uint tn = 0u; tn < TN; ++tn) result[tm] += inter[tn] * v[tn];
496
+ }
497
+ bn += BLOCKN;
498
+ }
499
+ for (uint tm = 0u; tm < TM; ++tm) {
500
+ for (ushort sn = 16; sn >= 1; sn >>= 1) {
501
+ result[tm] += simd_shuffle_down(result[tm], sn);
502
+ }
503
+ }
504
+ device atomic_float* ls = (device atomic_float*)logits_scratch;
505
+ if (lane == 0u) {
506
+ for (uint tm = 0u; tm < TM; ++tm) {
507
+ atomic_store_explicit(&ls[row0 + tm], result[tm],
508
+ memory_order_relaxed);
509
+ }
510
+ }
511
+
512
+ threadgroup_barrier(mem_flags::mem_device);
513
+ threadgroup uint last_flag;
514
+ if (tid == 0u) {
515
+ device atomic_uint* ctr = (device atomic_uint*)ctr_in;
516
+ uint prev = atomic_fetch_add_explicit(ctr, 1u, memory_order_relaxed);
517
+ uint last = (prev == NTG - 1u) ? 1u : 0u;
518
+ if (last == 1u) atomic_store_explicit(ctr, 0u, memory_order_relaxed);
519
+ last_flag = last;
520
+ }
521
+ threadgroup_barrier(mem_flags::mem_threadgroup);
522
+ if (last_flag == 0u) return;
523
+ threadgroup_barrier(mem_flags::mem_device);
524
+
525
+ float my_max = -1e30f;
526
+ for (uint e = tid; e < NE; e += n_threads) {
527
+ float v = atomic_load_explicit(&ls[e], memory_order_relaxed);
528
+ if (v > my_max) my_max = v;
529
+ }
530
+ for (int off = 16; off > 0; off >>= 1) {
531
+ float other = simd_shuffle_down(my_max, off);
532
+ if (other > my_max) my_max = other;
533
+ }
534
+ threadgroup float sg_red[16];
535
+ if (lane == 0u) sg_red[sg_id] = my_max;
536
+ threadgroup_barrier(mem_flags::mem_threadgroup);
537
+ if (tid == 0u) {
538
+ float m = sg_red[0];
539
+ for (uint s = 1u; s < n_sg; s++) if (sg_red[s] > m) m = sg_red[s];
540
+ sg_red[0] = m;
541
+ }
542
+ threadgroup_barrier(mem_flags::mem_threadgroup);
543
+ float lmax = sg_red[0];
544
+
545
+ threadgroup float scores[NE];
546
+ float my_sum = 0.0f;
547
+ for (uint e = tid; e < NE; e += n_threads) {
548
+ float lv = atomic_load_explicit(&ls[e], memory_order_relaxed);
549
+ float v = metal::exp(lv - lmax);
550
+ scores[e] = v;
551
+ my_sum += v;
552
+ }
553
+ for (int off = 16; off > 0; off >>= 1) {
554
+ my_sum += simd_shuffle_down(my_sum, off);
555
+ }
556
+ threadgroup_barrier(mem_flags::mem_threadgroup);
557
+ if (lane == 0u) sg_red[sg_id] = my_sum;
558
+ threadgroup_barrier(mem_flags::mem_threadgroup);
559
+ if (tid == 0u) {
560
+ float ssum = sg_red[0];
561
+ for (uint i = 1u; i < n_sg; i++) ssum += sg_red[i];
562
+ sg_red[0] = ssum;
563
+ }
564
+ threadgroup_barrier(mem_flags::mem_threadgroup);
565
+ float inv_total = 1.0f / (sg_red[0] + 1e-20f);
566
+ for (uint e = tid; e < NE; e += n_threads) {
567
+ scores[e] = scores[e] * inv_total;
568
+ }
569
+ threadgroup_barrier(mem_flags::mem_threadgroup);
570
+
571
+ threadgroup int topk_idx[8];
572
+ threadgroup float topk_val[8];
573
+ threadgroup uint8_t used[NE];
574
+ for (uint e = tid; e < NE; e += n_threads) used[e] = 0;
575
+ threadgroup_barrier(mem_flags::mem_threadgroup);
576
+
577
+ for (int k = 0; k < 8; k++) {
578
+ float my_best = -1e30f;
579
+ int my_idx = 0;
580
+ for (int e = int(tid); e < int(NE); e += int(n_threads)) {
581
+ if (!used[e] && scores[e] > my_best) {
582
+ my_best = scores[e];
583
+ my_idx = e;
584
+ }
585
+ }
586
+ for (int off = 16; off > 0; off >>= 1) {
587
+ float other_v = simd_shuffle_down(my_best, off);
588
+ int other_i = simd_shuffle_down(my_idx, off);
589
+ if (other_v > my_best) { my_best = other_v; my_idx = other_i; }
590
+ }
591
+ threadgroup float sg_vals[16];
592
+ threadgroup int sg_idxs[16];
593
+ if (lane == 0u) { sg_vals[sg_id] = my_best; sg_idxs[sg_id] = my_idx; }
594
+ threadgroup_barrier(mem_flags::mem_threadgroup);
595
+ if (tid == 0u) {
596
+ float bv = sg_vals[0]; int bi = sg_idxs[0];
597
+ for (uint s = 1u; s < n_sg; s++) {
598
+ if (sg_vals[s] > bv) { bv = sg_vals[s]; bi = sg_idxs[s]; }
599
+ }
600
+ topk_val[k] = bv; topk_idx[k] = bi;
601
+ used[bi] = 1;
602
+ }
603
+ threadgroup_barrier(mem_flags::mem_threadgroup);
604
+ }
605
+
606
+ if (tid < 8u) {
607
+ float sel_sum = 0.0f;
608
+ for (int i = 0; i < 8; i++) sel_sum += topk_val[i];
609
+ out_indices[tid] = topk_idx[tid];
610
+ out_scores[tid] = float(topk_val[tid] / (sel_sum + 1e-20f));
611
+ }
612
+ """
613
+ return mx.fast.metal_kernel(
614
+ name="maple_fused_router",
615
+ input_names=["x", "w", "ctr_in"],
616
+ output_names=["out_indices", "out_scores", "logits_scratch"],
617
+ source=source,
618
+ )
619
+
620
+
621
+ _fused_router_kernel = _make_fused_router_kernel()
622
+
623
+
624
+ class MapleGate(nn.Module):
625
+ def __init__(self, args: ModelArgs):
626
+ super().__init__()
627
+ self.top_k = args.num_experts_per_tok
628
+ self.num_experts = args.num_experts
629
+ self.hidden_size = args.hidden_size
630
+ # Kept as a raw parameter (not nn.Linear) so quantization never
631
+ # touches it. The matmul accumulates in float32 and selection runs on
632
+ # float32 scores.
633
+ self.weight = mx.zeros((args.num_experts, args.hidden_size))
634
+ self._router_ctr = None
635
+ self._fused = None # None = unprobed, then True/False
636
+
637
+ def _fused_call(self, x):
638
+ if self._router_ctr is None:
639
+ self._router_ctr = mx.zeros((8,), dtype=mx.uint32)
640
+ mx.eval(self._router_ctr)
641
+ inds, scores, _ = _fused_router_kernel(
642
+ inputs=[x.reshape(-1), self.weight, self._router_ctr],
643
+ template=[
644
+ ("T_", self.weight.dtype),
645
+ ("NEXP", self.num_experts),
646
+ ("DIM", self.hidden_size),
647
+ ],
648
+ grid=((self.num_experts // 32) * 256, 1, 1),
649
+ threadgroup=(256, 1, 1),
650
+ output_shapes=[(8,), (8,), (self.num_experts,)],
651
+ output_dtypes=[mx.int32, mx.float32, mx.float32],
652
+ )
653
+ shape = x.shape[:-1] + (self.top_k,)
654
+ return inds.reshape(shape), scores.reshape(shape)
655
+
656
+ def _reference(self, x):
657
+ # `router_dtype: fp32`. In bf16 the near-tied top-8 boundary flips a
658
+ # few percent of picks per layer, which compounds over 24 layers.
659
+ gates = x.astype(mx.float32) @ self.weight.astype(mx.float32).T
660
+ return group_expert_select(gates, self.top_k)
661
+
662
+ def _probe(self, x):
663
+ # Not _matches(): the two paths may order the selected experts
664
+ # differently, and an exact tie at the top-k boundary may legitimately
665
+ # pick either of the tied experts. Compare the sorted score vectors,
666
+ # and bound-check the ids since a bad one indexes the expert gather.
667
+ try:
668
+ inds, scores = self._fused_call(x)
669
+ ref_inds, ref_scores = self._reference(x)
670
+ mx.eval(inds, scores, ref_inds, ref_scores)
671
+ except Exception:
672
+ return False
673
+ return (
674
+ inds.shape == ref_inds.shape
675
+ and bool(mx.all((inds >= 0) & (inds < self.num_experts)))
676
+ and bool(mx.allclose(mx.sort(scores), mx.sort(ref_scores), atol=1e-5))
677
+ )
678
+
679
+ def __call__(self, x):
680
+ if self._fused is not False and x.size == self.hidden_size:
681
+ if self._fused is None:
682
+ self._fused = self._probe(x)
683
+ if self._fused:
684
+ return self._fused_call(x)
685
+ return self._reference(x)
686
+
687
+
688
+ @partial(mx.compile, shapeless=True)
689
+ def aggregate_expert_outputs(expert_outputs, scores):
690
+ # Combined in float32, rounded once at the end (reference `moe_infer`).
691
+ return (
692
+ (expert_outputs.astype(mx.float32) * scores[..., None])
693
+ .sum(axis=-2)
694
+ .astype(expert_outputs.dtype)
695
+ )
696
+
697
+
698
+ class MapleSwitchGLU(nn.Module):
699
+ """SwitchGLU with the up and gate projections fused into one gather
700
+ matmul; sanitize() concatenates the checkpoint's split tensors."""
701
+
702
+ def __init__(self, input_dims, hidden_dims, num_experts, bias=False):
703
+ super().__init__()
704
+ self.up_gate_proj = SwitchLinear(
705
+ input_dims, 2 * hidden_dims, num_experts, bias=bias
706
+ )
707
+ self.down_proj = SwitchLinear(hidden_dims, input_dims, num_experts, bias=bias)
708
+
709
+ def __call__(self, x, indices):
710
+ x = mx.expand_dims(x, (-2, -3))
711
+
712
+ do_sort = indices.size >= 64
713
+ idx = indices
714
+ inv_order = None
715
+ if do_sort:
716
+ x, idx, inv_order = _gather_sort(x, indices)
717
+
718
+ x_up, x_gate = mx.split(
719
+ self.up_gate_proj(x, idx, sorted_indices=do_sort), 2, axis=-1
720
+ )
721
+ x = self.down_proj(clamped_swiglu(x_gate, x_up), idx, sorted_indices=do_sort)
722
+
723
+ if do_sort:
724
+ x = _scatter_unsort(x, inv_order, indices.shape)
725
+
726
+ return x.squeeze(-2)
727
+
728
+
729
+ class MapleSparseMoeBlock(nn.Module):
730
+ def __init__(self, args: ModelArgs):
731
+ super().__init__()
732
+ self.gate = MapleGate(args)
733
+ self.switch_mlp = MapleSwitchGLU(
734
+ args.hidden_size,
735
+ args.moe_intermediate_size,
736
+ args.num_experts,
737
+ bias=args.use_bias,
738
+ )
739
+
740
+ def __call__(self, x):
741
+ inds, scores = self.gate(x)
742
+ y = self.switch_mlp(x, inds)
743
+ return aggregate_expert_outputs(y, scores)
744
+
745
+
746
+ class MapleDecoderLayer(nn.Module):
747
+ def __init__(self, args: ModelArgs, layer_idx: int):
748
+ super().__init__()
749
+ self.self_attn = MapleAttention(args, layer_idx)
750
+ self.mlp = (
751
+ MapleSparseMoeBlock(args)
752
+ if layer_idx >= args.first_k_dense_replace
753
+ else MapleMLP(args)
754
+ )
755
+ self.input_layernorm = MapleRMSNorm(args.hidden_size, eps=args.rms_norm_eps)
756
+ self.post_attention_layernorm = MapleRMSNorm(
757
+ args.hidden_size, eps=args.rms_norm_eps
758
+ )
759
+
760
+ def __call__(
761
+ self,
762
+ x: mx.array,
763
+ mask: Optional[mx.array] = None,
764
+ cache: Optional[Any] = None,
765
+ ) -> mx.array:
766
+ r = self.self_attn(self.input_layernorm(x), mask, cache)
767
+ h = x + r
768
+ r = self.mlp(self.post_attention_layernorm(h))
769
+ return h + r
770
+
771
+
772
+ class MapleModel(nn.Module):
773
+ def __init__(self, args: ModelArgs):
774
+ super().__init__()
775
+ self.args = args
776
+ self.word_embeddings = nn.Embedding(args.vocab_size, args.hidden_size)
777
+ self.layers = [
778
+ MapleDecoderLayer(args, layer_idx=i) for i in range(args.num_hidden_layers)
779
+ ]
780
+ self.norm = MapleRMSNorm(args.hidden_size, eps=args.rms_norm_eps)
781
+
782
+ self.layer_types = args.layer_types
783
+ self.window_size = args.sliding_window
784
+ self.swa_idx = (
785
+ self.layer_types.index("sliding_attention")
786
+ if "sliding_attention" in self.layer_types
787
+ else None
788
+ )
789
+ self.ga_idx = (
790
+ self.layer_types.index("full_attention")
791
+ if "full_attention" in self.layer_types
792
+ else None
793
+ )
794
+ self._fused_add_norm = None # None = unprobed, then True/False
795
+ self._zero = None
796
+
797
+ def _decode_fused(self, h, cache, full_mask, swa_mask):
798
+ """Decode loop with residual adds folded into the norms.
799
+
800
+ Carries (h, r) instead of adding r back each step, so every
801
+ add+norm pair is one dispatch. Identical arithmetic: the kernel
802
+ rounds the sum once (as the bf16 add did) and norms the rounded
803
+ stream with an fp32 weight multiply.
804
+ """
805
+ if self._zero is None:
806
+ self._zero = mx.zeros(h.shape, h.dtype)
807
+ mx.eval(self._zero)
808
+ r = self._zero # x + 0 is exact in bf16
809
+ for layer, c, layer_type in zip(self.layers, cache, self.layer_types):
810
+ mask = full_mask if layer_type == "full_attention" else swa_mask
811
+ ln = layer.input_layernorm
812
+ h, hn = _add_rms_norm(h, r, ln.weight, ln.eps)
813
+ r = layer.self_attn(hn, mask, c)
814
+ ln = layer.post_attention_layernorm
815
+ h, hn = _add_rms_norm(h, r, ln.weight, ln.eps)
816
+ r = layer.mlp(hn)
817
+ return _add_rms_norm(h, r, self.norm.weight, self.norm.eps)[1]
818
+
819
+ def __call__(
820
+ self,
821
+ inputs: mx.array,
822
+ cache: Optional[Any] = None,
823
+ ):
824
+ h = self.word_embeddings(inputs)
825
+
826
+ if cache is None:
827
+ cache = [None] * len(self.layers)
828
+
829
+ full_mask = None
830
+ swa_mask = None
831
+ if self.ga_idx is not None:
832
+ full_mask = create_attention_mask(h, cache[self.ga_idx])
833
+ if self.swa_idx is not None:
834
+ swa_mask = create_attention_mask(
835
+ h, cache[self.swa_idx], window_size=self.window_size
836
+ )
837
+
838
+ if h.size == h.shape[-1]:
839
+ if self._fused_add_norm is None:
840
+ self._fused_add_norm = _add_rms_norm_ok(
841
+ h.shape[-1], h.dtype, self.norm.weight, self.norm.eps
842
+ )
843
+ if self._fused_add_norm:
844
+ return self._decode_fused(h, cache, full_mask, swa_mask)
845
+
846
+ for layer, c, layer_type in zip(self.layers, cache, self.layer_types):
847
+ mask = full_mask if layer_type == "full_attention" else swa_mask
848
+ h = layer(h, mask, c)
849
+
850
+ return self.norm(h)
851
+
852
+
853
+ class FlashHead(nn.Module):
854
+ """Two-phase approximate lm_head for single-stream decode.
855
+
856
+ Phase one scores quantized cluster centroids of the vocabulary; phase two
857
+ computes exact logits only for the tokens of the top ``n_probes`` clusters
858
+ (plus a fixed set of forced control tokens such as EOS). All other logits
859
+ are -inf, so greedy decoding is exact whenever the true argmax lies in the
860
+ probed clusters. Prefill and batched calls use the exact lm_head.
861
+
862
+ Reference: FlashHead — Efficient Drop-in Replacement for the
863
+ Classification Head in Language Model Inference.
864
+ """
865
+
866
+ def __init__(self, args: ModelArgs):
867
+ super().__init__()
868
+ meta = args.flash_head
869
+ if not meta.get("scaled_centroids"):
870
+ raise ValueError(
871
+ "FlashHead metadata predates scaled centroids; regenerate with "
872
+ "`python -m mlx_lm.ternary <checkpoint> --flash-head-only`."
873
+ )
874
+ n_clusters = meta["n_clusters"]
875
+ cluster_size = meta["cluster_size"]
876
+ # Default matches the converter's `--probes` default; every generated
877
+ # checkpoint records the value explicitly.
878
+ self.n_probes = min(meta.get("n_probes", 512), n_clusters)
879
+ self.head_group_size = meta.get("head_group_size", 64)
880
+ self.head_bits = meta.get("head_bits", 4)
881
+ # Centroids are directions, pre-scaled at generation time by the
882
+ # largest lm_head row norm in their cluster: that upper-bounds the
883
+ # cluster's best logit, so high-frequency small-norm tokens are still
884
+ # probed, and scoring stays a single matmul.
885
+ self.centroids = nn.QuantizedLinear(
886
+ args.hidden_size,
887
+ n_clusters,
888
+ bias=False,
889
+ group_size=meta.get("group_size", 64),
890
+ bits=meta.get("bits", 4),
891
+ )
892
+ self.token_map = mx.zeros((n_clusters, cluster_size), dtype=mx.int32)
893
+ # Cluster-ordered copy of the quantized lm_head: subset logits are one
894
+ # gather_qmm over the probed 32-row blocks, with no per-step gather.
895
+ # It is a row-permutation of lm_head by token_map and nothing more, so
896
+ # it is derived rather than stored: Model.sanitize rebuilds it at load.
897
+ hidden = args.hidden_size
898
+ self.head = {
899
+ "weight": mx.zeros(
900
+ (n_clusters, cluster_size, hidden * self.head_bits // 32),
901
+ dtype=mx.uint32,
902
+ ),
903
+ "scales": mx.zeros(
904
+ (n_clusters, cluster_size, hidden // self.head_group_size),
905
+ dtype=mx.bfloat16,
906
+ ),
907
+ "biases": mx.zeros(
908
+ (n_clusters, cluster_size, hidden // self.head_group_size),
909
+ dtype=mx.bfloat16,
910
+ ),
911
+ }
912
+ self._force_ids = mx.array(meta.get("force_tokens", []), dtype=mx.int32)
913
+ self._force_rows = None
914
+
915
+ def __call__(self, h: mx.array, lm_head: nn.Module) -> mx.array:
916
+ hv = h[:, -1, :]
917
+ top = mx.argpartition(self.centroids(hv), kth=-self.n_probes, axis=-1)[
918
+ ..., -self.n_probes :
919
+ ] # [1, n_probes]
920
+ oids = self.token_map[top[0]].reshape(-1)
921
+
922
+ logits = mx.gather_qmm(
923
+ hv.reshape(1, 1, 1, 1, -1),
924
+ self.head["weight"],
925
+ self.head["scales"],
926
+ self.head["biases"],
927
+ rhs_indices=top[:, None, :],
928
+ transpose=True,
929
+ group_size=self.head_group_size,
930
+ bits=self.head_bits,
931
+ ).reshape(-1)
932
+
933
+ if self._force_ids.size:
934
+ if self._force_rows is None:
935
+ self._force_rows = (
936
+ lm_head.weight[self._force_ids],
937
+ lm_head.scales[self._force_ids],
938
+ lm_head.biases[self._force_ids],
939
+ )
940
+ mx.eval(*self._force_rows)
941
+ fw, fs, fb = self._force_rows
942
+ force_logits = mx.quantized_matmul(
943
+ hv,
944
+ fw,
945
+ scales=fs,
946
+ biases=fb,
947
+ transpose=True,
948
+ group_size=lm_head.group_size,
949
+ bits=lm_head.bits,
950
+ mode=getattr(lm_head, "mode", "affine"),
951
+ )[0]
952
+ oids = mx.concatenate([oids, self._force_ids])
953
+ logits = mx.concatenate([logits, force_logits])
954
+
955
+ vocab_size = lm_head.weight.shape[0]
956
+ full = mx.full((1, 1, vocab_size), float("-inf"), dtype=logits.dtype)
957
+ full[0, 0, oids] = logits
958
+ return full
959
+
960
+
961
+ class Model(nn.Module):
962
+ def __init__(self, args: ModelArgs):
963
+ super().__init__()
964
+ self.args = args
965
+ self.model_type = args.model_type
966
+ self.model = MapleModel(args)
967
+ if not args.tie_word_embeddings:
968
+ self.lm_head = nn.Linear(args.hidden_size, args.vocab_size, bias=False)
969
+ if args.flash_head and args.use_flash_head and not args.tie_word_embeddings:
970
+ self.lm_head_flash = FlashHead(args)
971
+ else:
972
+ self.lm_head_flash = None
973
+
974
+ def __call__(
975
+ self,
976
+ inputs: mx.array,
977
+ cache=None,
978
+ ):
979
+ out = self.model(inputs, cache)
980
+ if self.args.tie_word_embeddings:
981
+ return self.model.word_embeddings.as_linear(out)
982
+ if (
983
+ self.lm_head_flash is not None
984
+ and out.shape[0] == 1
985
+ and out.shape[1] == 1
986
+ and isinstance(self.lm_head, nn.QuantizedLinear)
987
+ and getattr(self.lm_head, "mode", "affine") == "affine"
988
+ ):
989
+ return self.lm_head_flash(out, self.lm_head)
990
+ return self.lm_head(out)
991
+
992
+ def sanitize(self, weights):
993
+ if self.args.tie_word_embeddings:
994
+ # Drop the head entirely (weight + quantization scales/biases).
995
+ weights = {k: v for k, v in weights.items() if not k.startswith("lm_head.")}
996
+
997
+ # FlashHead disabled (e.g. model_config={"flash_head": None}): drop its
998
+ # tensors so checkpoints that carry them still load.
999
+ if self.lm_head_flash is None:
1000
+ weights = {
1001
+ k: v for k, v in weights.items() if not k.startswith("lm_head_flash.")
1002
+ }
1003
+ else:
1004
+ # Folded into the centroid rows at generation time; older shards
1005
+ # still carry the tensor.
1006
+ weights.pop("lm_head_flash.cluster_scale", None)
1007
+ # `lm_head_flash.head.*` is lm_head permuted by token_map (see
1008
+ # mlx_lm.ternary.generate_flash_head), so it is pure redundancy on
1009
+ # disk. Checkpoints may ship it or omit it; reconcile both here.
1010
+ if "lm_head_flash.head.weight" not in weights:
1011
+ token_map = weights["lm_head_flash.token_map"]
1012
+ order = token_map.reshape(-1)
1013
+ for k in ("weight", "scales", "biases"):
1014
+ weights[f"lm_head_flash.head.{k}"] = weights[f"lm_head.{k}"][
1015
+ order
1016
+ ].reshape(*token_map.shape, -1)
1017
+
1018
+ # Ternary tensors carry one scale per output row, so checkpoints store
1019
+ # it once as `row_alpha` and omit biases entirely (bias == -scale).
1020
+ # Expand here so everything downstream — fusion below, and mlx's own
1021
+ # quantized kernels — sees the per-group layout. Checkpoints written
1022
+ # with `--group-scales` have no row_alpha and pass straight through.
1023
+ row_alpha_keys = [k for k in weights if k.endswith(".row_alpha")]
1024
+ if row_alpha_keys:
1025
+ group_size = (self.args.quantization or {}).get("group_size", 128)
1026
+ for key in row_alpha_keys:
1027
+ alpha = weights.pop(key)
1028
+ prefix = key[: -len(".row_alpha")]
1029
+ packed = weights.get(f"{prefix}.weight")
1030
+ if packed is None:
1031
+ continue
1032
+ # 2-bit packing stores 16 codes per uint32 word.
1033
+ n_groups = (packed.shape[-1] * 16) // group_size
1034
+ scales = mx.contiguous(
1035
+ mx.broadcast_to(alpha[..., None], (*alpha.shape, n_groups))
1036
+ )
1037
+ weights[f"{prefix}.scales"] = scales
1038
+ weights[f"{prefix}.biases"] = -scales
1039
+
1040
+ # Stack per-expert weights from the Hugging Face layout into the
1041
+ # SwitchGLU layout. Already-converted checkpoints pass through.
1042
+ for l in range(self.args.num_hidden_layers):
1043
+ prefix = f"model.layers.{l}"
1044
+ for m in ["gate_proj", "down_proj", "up_proj"]:
1045
+ for k in ["weight", "scales", "biases", "bias"]:
1046
+ if f"{prefix}.mlp.experts.0.{m}.{k}" in weights:
1047
+ to_join = [
1048
+ weights.pop(f"{prefix}.mlp.experts.{e}.{m}.{k}")
1049
+ for e in range(self.args.num_experts)
1050
+ ]
1051
+ weights[f"{prefix}.mlp.switch_mlp.{m}.{k}"] = mx.stack(to_join)
1052
+
1053
+ # Fuse split projections: q/k/v -> qkv_proj (rows), MoE up/gate ->
1054
+ # up_gate_proj (per-expert rows). Row-wise quantized tensors
1055
+ # (weight/scales/biases) concatenate losslessly along the output
1056
+ # axis.
1057
+ for suffix in ["weight", "scales", "biases", "bias"]:
1058
+ qkv = [
1059
+ f"{prefix}.self_attn.{p}.{suffix}"
1060
+ for p in ("q_proj", "k_proj", "v_proj")
1061
+ ]
1062
+ if qkv[0] in weights:
1063
+ weights[f"{prefix}.self_attn.qkv_proj.{suffix}"] = mx.concatenate(
1064
+ [weights.pop(k) for k in qkv], axis=0
1065
+ )
1066
+ up = f"{prefix}.mlp.switch_mlp.up_proj.{suffix}"
1067
+ gate = f"{prefix}.mlp.switch_mlp.gate_proj.{suffix}"
1068
+ if up in weights:
1069
+ weights[f"{prefix}.mlp.switch_mlp.up_gate_proj.{suffix}"] = (
1070
+ mx.concatenate([weights.pop(up), weights.pop(gate)], axis=1)
1071
+ )
1072
+
1073
+ return weights
1074
+
1075
+ def make_cache(self):
1076
+ caches = []
1077
+ for layer_type in self.model.layer_types:
1078
+ if layer_type == "sliding_attention":
1079
+ caches.append(RotatingKVCache(max_size=self.args.sliding_window))
1080
+ else:
1081
+ caches.append(KVCache())
1082
+ return caches
1083
+
1084
+ @property
1085
+ def layers(self):
1086
+ return self.model.layers
1087
+
1088
+ @property
1089
+ def quant_predicate(self):
1090
+ def predicate(path, _):
1091
+ if path.endswith("lm_head") or "word_embeddings" in path:
1092
+ return {"group_size": 64, "bits": 4}
1093
+ return True
1094
+
1095
+ return predicate
merges.txt ADDED
The diff for this file is too large to render. See raw diff
 
model-00001-of-00005.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7b3e661e2d9f7a532e6a094ca21d0f9099f51c0a5265087c028e1e5bed0792d6
3
+ size 5001476211
model-00002-of-00005.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0e4c561046e5e034d7705e1f79f46cda5e7ed66a3ab571deb35aec44fb081723
3
+ size 5001766873
model-00003-of-00005.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5736cb77da1788d0d1a69f40f422acec50d1eaeaa6347977fed12f6a66b1802f
3
+ size 5001774872
model-00004-of-00005.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:db44867a4a00e97820b4d957b11f99635fb941f13f777cddb2b19ae7cfb3571a
3
+ size 5001775453
model-00005-of-00005.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:908b54560ed2b2fbdfeb3a3ef233a83c0d323e8318a8bbcb2b4cd6036ba90846
3
+ size 1468680306
model.safetensors.index.json ADDED
The diff for this file is too large to render. See raw diff
 
oq_imatrix_report.json ADDED
The diff for this file is too large to render. See raw diff
 
special_tokens_map.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "additional_special_tokens": [
3
+ "<|im_start|>",
4
+ "<|im_end|>",
5
+ "<|object_ref_start|>",
6
+ "<|object_ref_end|>",
7
+ "<|box_start|>",
8
+ "<|box_end|>",
9
+ "<|quad_start|>",
10
+ "<|quad_end|>",
11
+ "<|vision_start|>",
12
+ "<|vision_end|>",
13
+ "<|vision_pad|>",
14
+ "<|image_pad|>",
15
+ "<|video_pad|>"
16
+ ],
17
+ "eos_token": {
18
+ "content": "<|im_end|>",
19
+ "lstrip": false,
20
+ "normalized": false,
21
+ "rstrip": false,
22
+ "single_word": false
23
+ },
24
+ "pad_token": {
25
+ "content": "<|endoftext|>",
26
+ "lstrip": false,
27
+ "normalized": false,
28
+ "rstrip": false,
29
+ "single_word": false
30
+ }
31
+ }
tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:aeb13307a71acd8fe81861d94ad54ab689df773318809eed3cbe794b4492dae4
3
+ size 11422654
tokenizer_config.json ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_bos_token": false,
3
+ "add_prefix_space": false,
4
+ "added_tokens_decoder": {
5
+ "151643": {
6
+ "content": "<|endoftext|>",
7
+ "lstrip": false,
8
+ "normalized": false,
9
+ "rstrip": false,
10
+ "single_word": false,
11
+ "special": true
12
+ },
13
+ "151644": {
14
+ "content": "<|im_start|>",
15
+ "lstrip": false,
16
+ "normalized": false,
17
+ "rstrip": false,
18
+ "single_word": false,
19
+ "special": true
20
+ },
21
+ "151645": {
22
+ "content": "<|im_end|>",
23
+ "lstrip": false,
24
+ "normalized": false,
25
+ "rstrip": false,
26
+ "single_word": false,
27
+ "special": true
28
+ },
29
+ "151646": {
30
+ "content": "<|object_ref_start|>",
31
+ "lstrip": false,
32
+ "normalized": false,
33
+ "rstrip": false,
34
+ "single_word": false,
35
+ "special": true
36
+ },
37
+ "151647": {
38
+ "content": "<|object_ref_end|>",
39
+ "lstrip": false,
40
+ "normalized": false,
41
+ "rstrip": false,
42
+ "single_word": false,
43
+ "special": true
44
+ },
45
+ "151648": {
46
+ "content": "<|box_start|>",
47
+ "lstrip": false,
48
+ "normalized": false,
49
+ "rstrip": false,
50
+ "single_word": false,
51
+ "special": true
52
+ },
53
+ "151649": {
54
+ "content": "<|box_end|>",
55
+ "lstrip": false,
56
+ "normalized": false,
57
+ "rstrip": false,
58
+ "single_word": false,
59
+ "special": true
60
+ },
61
+ "151650": {
62
+ "content": "<|quad_start|>",
63
+ "lstrip": false,
64
+ "normalized": false,
65
+ "rstrip": false,
66
+ "single_word": false,
67
+ "special": true
68
+ },
69
+ "151651": {
70
+ "content": "<|quad_end|>",
71
+ "lstrip": false,
72
+ "normalized": false,
73
+ "rstrip": false,
74
+ "single_word": false,
75
+ "special": true
76
+ },
77
+ "151652": {
78
+ "content": "<|vision_start|>",
79
+ "lstrip": false,
80
+ "normalized": false,
81
+ "rstrip": false,
82
+ "single_word": false,
83
+ "special": true
84
+ },
85
+ "151653": {
86
+ "content": "<|vision_end|>",
87
+ "lstrip": false,
88
+ "normalized": false,
89
+ "rstrip": false,
90
+ "single_word": false,
91
+ "special": true
92
+ },
93
+ "151654": {
94
+ "content": "<|vision_pad|>",
95
+ "lstrip": false,
96
+ "normalized": false,
97
+ "rstrip": false,
98
+ "single_word": false,
99
+ "special": true
100
+ },
101
+ "151655": {
102
+ "content": "<|image_pad|>",
103
+ "lstrip": false,
104
+ "normalized": false,
105
+ "rstrip": false,
106
+ "single_word": false,
107
+ "special": true
108
+ },
109
+ "151656": {
110
+ "content": "<|video_pad|>",
111
+ "lstrip": false,
112
+ "normalized": false,
113
+ "rstrip": false,
114
+ "single_word": false,
115
+ "special": true
116
+ },
117
+ "151657": {
118
+ "content": "<tool_call>",
119
+ "lstrip": false,
120
+ "normalized": false,
121
+ "rstrip": false,
122
+ "single_word": false,
123
+ "special": false
124
+ },
125
+ "151658": {
126
+ "content": "</tool_call>",
127
+ "lstrip": false,
128
+ "normalized": false,
129
+ "rstrip": false,
130
+ "single_word": false,
131
+ "special": false
132
+ },
133
+ "151659": {
134
+ "content": "<|fim_prefix|>",
135
+ "lstrip": false,
136
+ "normalized": false,
137
+ "rstrip": false,
138
+ "single_word": false,
139
+ "special": false
140
+ },
141
+ "151660": {
142
+ "content": "<|fim_middle|>",
143
+ "lstrip": false,
144
+ "normalized": false,
145
+ "rstrip": false,
146
+ "single_word": false,
147
+ "special": false
148
+ },
149
+ "151661": {
150
+ "content": "<|fim_suffix|>",
151
+ "lstrip": false,
152
+ "normalized": false,
153
+ "rstrip": false,
154
+ "single_word": false,
155
+ "special": false
156
+ },
157
+ "151662": {
158
+ "content": "<|fim_pad|>",
159
+ "lstrip": false,
160
+ "normalized": false,
161
+ "rstrip": false,
162
+ "single_word": false,
163
+ "special": false
164
+ },
165
+ "151663": {
166
+ "content": "<|repo_name|>",
167
+ "lstrip": false,
168
+ "normalized": false,
169
+ "rstrip": false,
170
+ "single_word": false,
171
+ "special": false
172
+ },
173
+ "151664": {
174
+ "content": "<|file_sep|>",
175
+ "lstrip": false,
176
+ "normalized": false,
177
+ "rstrip": false,
178
+ "single_word": false,
179
+ "special": false
180
+ },
181
+ "151665": {
182
+ "content": "<tool_response>",
183
+ "lstrip": false,
184
+ "normalized": false,
185
+ "rstrip": false,
186
+ "single_word": false,
187
+ "special": false
188
+ },
189
+ "151666": {
190
+ "content": "</tool_response>",
191
+ "lstrip": false,
192
+ "normalized": false,
193
+ "rstrip": false,
194
+ "single_word": false,
195
+ "special": false
196
+ },
197
+ "151667": {
198
+ "content": "<think>",
199
+ "lstrip": false,
200
+ "normalized": false,
201
+ "rstrip": false,
202
+ "single_word": false,
203
+ "special": false
204
+ },
205
+ "151668": {
206
+ "content": "</think>",
207
+ "lstrip": false,
208
+ "normalized": false,
209
+ "rstrip": false,
210
+ "single_word": false,
211
+ "special": false
212
+ }
213
+ },
214
+ "additional_special_tokens": [
215
+ "<|im_start|>",
216
+ "<|im_end|>",
217
+ "<|object_ref_start|>",
218
+ "<|object_ref_end|>",
219
+ "<|box_start|>",
220
+ "<|box_end|>",
221
+ "<|quad_start|>",
222
+ "<|quad_end|>",
223
+ "<|vision_start|>",
224
+ "<|vision_end|>",
225
+ "<|vision_pad|>",
226
+ "<|image_pad|>",
227
+ "<|video_pad|>"
228
+ ],
229
+ "bos_token": null,
230
+ "clean_up_tokenization_spaces": false,
231
+ "eos_token": "<|im_end|>",
232
+ "errors": "replace",
233
+ "extra_special_tokens": {},
234
+ "model_max_length": 1010000,
235
+ "pad_token": "<|endoftext|>",
236
+ "padding_side": "right",
237
+ "split_special_tokens": false,
238
+ "tokenizer_class": "Qwen2Tokenizer",
239
+ "unk_token": null
240
+ }
vocab.json ADDED
The diff for this file is too large to render. See raw diff