1use crate::model_plan::{ModelPlan, OperationKind, OperationSupport, PlanCapabilities};
8use sha2::{Digest, Sha256};
9use std::collections::BTreeSet;
10use std::fmt::Write as _;
11use std::path::Path;
12
13#[derive(Clone, Copy)]
14pub struct KernelManifest {
15 pub name: &'static str,
16 support: fn(OperationKind) -> OperationSupport,
17}
18
19impl KernelManifest {
20 pub const fn new(name: &'static str, support: fn(OperationKind) -> OperationSupport) -> Self {
21 Self { name, support }
22 }
23
24 pub fn support(self, operation: OperationKind) -> OperationSupport {
25 (self.support)(operation)
26 }
27
28 pub fn capabilities(self, plan: &ModelPlan) -> PlanCapabilities {
29 plan.derive_capabilities(|operation| self.support(operation))
30 }
31
32 pub fn trunk_capabilities(self, plan: &ModelPlan) -> PlanCapabilities {
33 plan.derive_trunk_capabilities(|operation| self.support(operation))
34 }
35
36 pub fn multimodal_prefill_capabilities(self, plan: &ModelPlan) -> PlanCapabilities {
37 plan.derive_multimodal_prefill_capabilities(|operation| self.support(operation))
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
42pub enum RewriteSurface {
43 CarriedPrime,
44 DecodeEager,
45 DecodeBatch,
46 DecodeGraph,
47 MtpSpec,
48 Glm5Spec,
53 Pipeline,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct RewriteQualifications {
58 pub plan_sha256: String,
59 passed: BTreeSet<RewriteSurface>,
60}
61
62impl RewriteQualifications {
63 pub fn load(bundle: &Path, plan: &ModelPlan) -> Result<Self, String> {
64 let expected = execution_rewrites(plan);
65 let artifact_lock = std::fs::read(bundle.join("artifact.lock"))
66 .map_err(|error| format!("read artifact.lock: {error}"))?;
67 let artifact_lock_sha256 = hex_sha256(&artifact_lock);
68 let index = std::fs::read_to_string(bundle.join("rewrite-receipts.tsv"))
69 .map_err(|error| format!("read rewrite receipt index: {error}"))?;
70 let mut passed = BTreeSet::new();
71 for line in index.lines().skip(1) {
72 let columns: Vec<_> = line.split('\t').collect();
73 if columns.len() != 4 || columns[3] != "passed" {
74 return Err(format!("malformed rewrite receipt index row {line:?}"));
75 }
76 let rewrite = expected
77 .iter()
78 .find(|rewrite| rewrite.id == columns[0])
79 .ok_or_else(|| format!("receipt names unknown rewrite {}", columns[0]))?;
80 if !rewrite.eligible() || columns[1] != rewrite.plan_sha256 {
81 return Err(format!(
82 "receipt {} is not eligible for plan {}",
83 rewrite.id, rewrite.plan_sha256
84 ));
85 }
86 let receipt_path = bundle
87 .join("rewrite-receipts")
88 .join(format!("{}.tsv", rewrite.id));
89 let receipt = std::fs::read(&receipt_path)
90 .map_err(|error| format!("read {}: {error}", receipt_path.display()))?;
91 if hex_sha256(&receipt) != columns[2] {
92 return Err(format!("rewrite receipt hash mismatch for {}", rewrite.id));
93 }
94 let text = std::str::from_utf8(&receipt)
95 .map_err(|error| format!("rewrite receipt is not UTF-8: {error}"))?;
96 for (key, value) in [
97 ("status", "passed"),
98 ("rewrite", rewrite.id),
99 ("surface", rewrite.surface.as_str()),
100 ("implementation", rewrite.implementation),
101 ("plan_sha256", rewrite.plan_sha256.as_str()),
102 ("artifact_lock_sha256", artifact_lock_sha256.as_str()),
103 ("first_violation", "none"),
104 ] {
105 if !text.lines().any(|line| line == format!("{key}\t{value}")) {
106 return Err(format!(
107 "rewrite receipt {} does not bind {key}={value}",
108 rewrite.id
109 ));
110 }
111 }
112 passed.insert(rewrite.surface);
113 }
114 Ok(Self {
115 plan_sha256: plan_sha256(plan),
116 passed,
117 })
118 }
119
120 pub fn allows(&self, surface: RewriteSurface) -> bool {
121 self.passed.contains(&surface)
122 }
123
124 pub fn all_eligible(&self, plan: &ModelPlan) -> bool {
125 execution_rewrites(plan)
126 .into_iter()
127 .filter(ExecutionRewrite::eligible)
128 .all(|rewrite| self.allows(rewrite.surface))
129 }
130}
131
132impl RewriteSurface {
133 pub const fn as_str(self) -> &'static str {
134 match self {
135 Self::CarriedPrime => "carried-prime",
136 Self::DecodeEager => "decode-eager",
137 Self::DecodeBatch => "decode-batch",
138 Self::DecodeGraph => "decode-graph",
139 Self::MtpSpec => "mtp-spec",
140 Self::Glm5Spec => "glm5-spec",
141 Self::Pipeline => "pipeline",
142 }
143 }
144}
145
146#[derive(Debug, Clone, PartialEq, Eq)]
147pub struct ExecutionRewrite {
148 pub id: &'static str,
149 pub surface: RewriteSurface,
150 pub implementation: &'static str,
151 pub plan_sha256: String,
152 pub canonical_operations: Vec<OperationKind>,
153 pub blockers: Vec<OperationKind>,
154}
155
156impl ExecutionRewrite {
157 pub fn eligible(&self) -> bool {
158 self.blockers.is_empty()
159 }
160
161 pub fn verify_logits(
162 &self,
163 implementation_sha256: &str,
164 reference: &[f32],
165 candidate: &[f32],
166 policy: RewriteParityPolicy,
167 ) -> Result<RewriteParityReceipt, String> {
168 if !self.eligible() {
169 return Err(format!(
170 "rewrite {} is blocked by {:?}",
171 self.id, self.blockers
172 ));
173 }
174 if !is_sha256(implementation_sha256) {
175 return Err("rewrite implementation identity must be a lowercase SHA-256".into());
176 }
177 if reference.len() != candidate.len() || reference.is_empty() {
178 return Err(format!(
179 "rewrite parity requires equal non-empty streams (reference={} candidate={})",
180 reference.len(),
181 candidate.len()
182 ));
183 }
184 let mut max_abs = 0.0f32;
185 let mut max_rel = 0.0f32;
186 let mut first_violation = None;
187 for (index, (&expected, &actual)) in reference.iter().zip(candidate).enumerate() {
188 if !expected.is_finite() || !actual.is_finite() {
189 return Err(format!("rewrite parity has a non-finite value at {index}"));
190 }
191 let absolute = (expected - actual).abs();
192 let relative = absolute / expected.abs().max(1e-6);
193 max_abs = max_abs.max(absolute);
194 max_rel = max_rel.max(relative);
195 let allowed = policy.max_abs + policy.max_rel * expected.abs();
196 if absolute > allowed && first_violation.is_none() {
197 first_violation = Some(index);
198 }
199 }
200 let reference_argmax = stable_argmax(reference);
201 let candidate_argmax = stable_argmax(candidate);
202 let passed = first_violation.is_none()
203 && (!policy.require_argmax || reference_argmax == candidate_argmax);
204 Ok(RewriteParityReceipt {
205 rewrite_id: self.id,
206 surface: self.surface,
207 implementation: self.implementation,
208 implementation_sha256: implementation_sha256.to_string(),
209 plan_sha256: self.plan_sha256.clone(),
210 artifact_lock_sha256: None,
211 reference_sha256: f32_stream_sha256(reference),
212 candidate_sha256: f32_stream_sha256(candidate),
213 value_kind: RewriteValueKind::LogitsF32,
214 values: reference.len(),
215 max_abs,
216 max_rel,
217 reference_argmax,
218 candidate_argmax,
219 policy,
220 passed,
221 first_violation,
222 })
223 }
224
225 pub fn verify_tokens(
226 &self,
227 implementation_sha256: &str,
228 reference: &[u32],
229 candidate: &[u32],
230 ) -> Result<RewriteParityReceipt, String> {
231 if !self.eligible() {
232 return Err(format!(
233 "rewrite {} is blocked by {:?}",
234 self.id, self.blockers
235 ));
236 }
237 if !is_sha256(implementation_sha256) {
238 return Err("rewrite implementation identity must be a lowercase SHA-256".into());
239 }
240 if reference.len() != candidate.len() || reference.is_empty() {
241 return Err(format!(
242 "rewrite parity requires equal non-empty token streams (reference={} candidate={})",
243 reference.len(),
244 candidate.len()
245 ));
246 }
247 let first_violation = reference
248 .iter()
249 .zip(candidate)
250 .position(|(expected, actual)| expected != actual);
251 let max_abs = reference
252 .iter()
253 .zip(candidate)
254 .map(|(&expected, &actual)| expected.abs_diff(actual) as f32)
255 .fold(0.0f32, f32::max);
256 Ok(RewriteParityReceipt {
257 rewrite_id: self.id,
258 surface: self.surface,
259 implementation: self.implementation,
260 implementation_sha256: implementation_sha256.to_string(),
261 plan_sha256: self.plan_sha256.clone(),
262 artifact_lock_sha256: None,
263 reference_sha256: u32_stream_sha256(reference),
264 candidate_sha256: u32_stream_sha256(candidate),
265 value_kind: RewriteValueKind::TokenIdsU32,
266 values: reference.len(),
267 max_abs,
268 max_rel: 0.0,
269 reference_argmax: 0,
270 candidate_argmax: 0,
271 policy: RewriteParityPolicy {
272 max_abs: 0.0,
273 max_rel: 0.0,
274 require_argmax: false,
275 },
276 passed: first_violation.is_none(),
277 first_violation,
278 })
279 }
280}
281
282#[derive(Debug, Clone, Copy, PartialEq)]
283pub struct RewriteParityPolicy {
284 pub max_abs: f32,
285 pub max_rel: f32,
286 pub require_argmax: bool,
287}
288
289#[derive(Debug, Clone, PartialEq)]
290pub struct RewriteParityReceipt {
291 pub rewrite_id: &'static str,
292 pub surface: RewriteSurface,
293 pub implementation: &'static str,
294 pub implementation_sha256: String,
295 pub plan_sha256: String,
296 pub artifact_lock_sha256: Option<String>,
297 pub reference_sha256: String,
298 pub candidate_sha256: String,
299 pub value_kind: RewriteValueKind,
300 pub values: usize,
301 pub max_abs: f32,
302 pub max_rel: f32,
303 pub reference_argmax: usize,
304 pub candidate_argmax: usize,
305 pub policy: RewriteParityPolicy,
306 pub passed: bool,
307 pub first_violation: Option<usize>,
308}
309
310#[derive(Debug, Clone, Copy, PartialEq, Eq)]
311pub enum RewriteValueKind {
312 LogitsF32,
313 TokenIdsU32,
314}
315
316impl RewriteValueKind {
317 pub const fn as_str(self) -> &'static str {
318 match self {
319 Self::LogitsF32 => "logits-f32",
320 Self::TokenIdsU32 => "token-ids-u32",
321 }
322 }
323}
324
325impl RewriteParityReceipt {
326 pub fn bind_artifact_lock(mut self, artifact_lock: &[u8]) -> Self {
327 self.artifact_lock_sha256 = Some(hex_sha256(artifact_lock));
328 self
329 }
330
331 pub fn validate_for(&self, rewrite: &ExecutionRewrite) -> Result<(), String> {
332 if self.rewrite_id != rewrite.id
333 || self.surface != rewrite.surface
334 || self.implementation != rewrite.implementation
335 || self.plan_sha256 != rewrite.plan_sha256
336 {
337 return Err(format!(
338 "rewrite receipt identity does not match {} for plan {}",
339 rewrite.id, rewrite.plan_sha256
340 ));
341 }
342 if !rewrite.eligible() {
343 return Err(format!("rewrite {} is no longer eligible", rewrite.id));
344 }
345 if !self.passed {
346 return Err(format!("rewrite {} parity receipt failed", rewrite.id));
347 }
348 Ok(())
349 }
350
351 pub fn to_tsv(&self) -> String {
352 let mut output = String::new();
353 writeln!(output, "format\tmemra-rewrite-parity-v1").unwrap();
354 writeln!(
355 output,
356 "status\t{}",
357 if self.passed { "passed" } else { "failed" }
358 )
359 .unwrap();
360 writeln!(output, "rewrite\t{}", self.rewrite_id).unwrap();
361 writeln!(output, "surface\t{}", self.surface.as_str()).unwrap();
362 writeln!(output, "implementation\t{}", self.implementation).unwrap();
363 writeln!(
364 output,
365 "implementation_sha256\t{}",
366 self.implementation_sha256
367 )
368 .unwrap();
369 writeln!(output, "plan_sha256\t{}", self.plan_sha256).unwrap();
370 if let Some(hash) = self.artifact_lock_sha256.as_ref() {
371 writeln!(output, "artifact_lock_sha256\t{hash}").unwrap();
372 }
373 writeln!(output, "reference_sha256\t{}", self.reference_sha256).unwrap();
374 writeln!(output, "candidate_sha256\t{}", self.candidate_sha256).unwrap();
375 writeln!(output, "value_kind\t{}", self.value_kind.as_str()).unwrap();
376 writeln!(output, "values\t{}", self.values).unwrap();
377 writeln!(output, "max_abs\t{}", self.max_abs).unwrap();
378 writeln!(output, "max_rel\t{}", self.max_rel).unwrap();
379 writeln!(output, "reference_argmax\t{}", self.reference_argmax).unwrap();
380 writeln!(output, "candidate_argmax\t{}", self.candidate_argmax).unwrap();
381 writeln!(output, "atol\t{}", self.policy.max_abs).unwrap();
382 writeln!(output, "rtol\t{}", self.policy.max_rel).unwrap();
383 writeln!(output, "require_argmax\t{}", self.policy.require_argmax).unwrap();
384 writeln!(
385 output,
386 "first_violation\t{}",
387 self.first_violation
388 .map_or_else(|| "none".to_string(), |index| index.to_string())
389 )
390 .unwrap();
391 output
392 }
393}
394
395pub fn execution_rewrites(plan: &ModelPlan) -> Vec<ExecutionRewrite> {
396 let plan_sha256 = plan_sha256(plan);
397 let trunk = plan.trunk_operations();
398 let mut spec_operations = plan
399 .draft_operations()
400 .unwrap_or_else(|| vec![OperationKind::DraftPlan]);
401 spec_operations.extend(plan.trunk_operations());
402 let selections = [
403 (
404 "carried-prime.v1",
405 RewriteSurface::CarriedPrime,
406 CARRIED_PRIME,
407 trunk.clone(),
408 CARRIED_PRIME.trunk_capabilities(plan).batch,
409 ),
410 (
411 "decode-eager.v1",
412 RewriteSurface::DecodeEager,
413 NATIVE_EAGER,
414 trunk.clone(),
415 NATIVE_EAGER.trunk_capabilities(plan).batch,
416 ),
417 (
418 "decode-batch.v1",
419 RewriteSurface::DecodeBatch,
420 DECODE_BATCH,
421 trunk.clone(),
422 DECODE_BATCH.trunk_capabilities(plan).batch,
423 ),
424 (
425 "decode-graph.v1",
426 RewriteSurface::DecodeGraph,
427 DECODE_GRAPH,
428 trunk.clone(),
429 DECODE_GRAPH.trunk_capabilities(plan).cuda_graph,
430 ),
431 (
432 "mtp-spec.v1",
433 RewriteSurface::MtpSpec,
434 MTP_SPEC,
435 spec_operations.clone(),
436 MTP_SPEC.capabilities(plan).speculative,
437 ),
438 (
439 "glm5-spec.v1",
440 RewriteSurface::Glm5Spec,
441 GLM5_SPEC,
442 spec_operations,
443 GLM5_SPEC.capabilities(plan).speculative,
444 ),
445 (
446 "pipeline.v1",
447 RewriteSurface::Pipeline,
448 PIPELINE,
449 trunk,
450 PIPELINE.capabilities(plan).pipeline,
451 ),
452 ];
453 selections
454 .into_iter()
455 .map(
456 |(id, surface, manifest, canonical_operations, capability)| ExecutionRewrite {
457 id,
458 surface,
459 implementation: manifest.name,
460 plan_sha256: plan_sha256.clone(),
461 canonical_operations,
462 blockers: capability.blockers,
463 },
464 )
465 .collect()
466}
467
468fn plan_sha256(plan: &ModelPlan) -> String {
469 hex_sha256(format!("{plan:#?}\n").as_bytes())
470}
471
472fn f32_stream_sha256(values: &[f32]) -> String {
473 let mut bytes = Vec::with_capacity(values.len() * 4);
474 for value in values {
475 bytes.extend_from_slice(&value.to_bits().to_le_bytes());
476 }
477 hex_sha256(&bytes)
478}
479
480fn u32_stream_sha256(values: &[u32]) -> String {
481 let mut bytes = Vec::with_capacity(values.len() * 4);
482 for value in values {
483 bytes.extend_from_slice(&value.to_le_bytes());
484 }
485 hex_sha256(&bytes)
486}
487
488fn hex_sha256(bytes: &[u8]) -> String {
489 let digest = Sha256::digest(bytes);
490 digest.iter().map(|byte| format!("{byte:02x}")).collect()
491}
492
493fn is_sha256(value: &str) -> bool {
494 value.len() == 64
495 && value
496 .bytes()
497 .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
498}
499
500fn stable_argmax(values: &[f32]) -> usize {
501 values
502 .iter()
503 .enumerate()
504 .max_by(|(left_index, left), (right_index, right)| {
505 left.total_cmp(right)
506 .then_with(|| right_index.cmp(left_index))
507 })
508 .map(|(index, _)| index)
509 .unwrap_or(0)
510}
511
512fn carried_prime_support(operation: OperationKind) -> OperationSupport {
513 let mut support = OperationSupport::none();
514 support.batch = matches!(
515 operation,
516 OperationKind::Embedding
517 | OperationKind::RmsNorm
518 | OperationKind::FullAttention
519 | OperationKind::GatedDeltaNet
520 | OperationKind::FusedAttentionGate
521 | OperationKind::DenseMlp
522 | OperationKind::SiluActivation
523 | OperationKind::SerialResidual
524 | OperationKind::KvState
525 | OperationKind::RecurrentState
526 | OperationKind::LogitsMask
527 | OperationKind::OutputProjection
528 );
529 support
530}
531
532pub const CARRIED_PRIME: KernelManifest =
533 KernelManifest::new("carried-prime-batch", carried_prime_support);
534
535#[derive(Debug, Clone, Copy, PartialEq, Eq)]
536pub enum DecodeBatchProgram {
537 Generic,
538 SlidingGatedMoe,
539 Gemma,
540}
541
542pub fn decode_batch_program(plan: &ModelPlan) -> DecodeBatchProgram {
543 let operations = plan.trunk_operations();
544 if operations.contains(&OperationKind::GemmaResidual)
545 || operations.contains(&OperationKind::GemmaParallelMoeResidual)
546 {
547 DecodeBatchProgram::Gemma
548 } else if operations.contains(&OperationKind::SlidingWindowAttention)
549 && operations.contains(&OperationKind::SeparateAttentionGate)
550 && operations.contains(&OperationKind::MoeMlp)
551 && operations.contains(&OperationKind::SigmoidRouter)
552 {
553 DecodeBatchProgram::SlidingGatedMoe
554 } else {
555 DecodeBatchProgram::Generic
556 }
557}
558
559pub fn decode_batch_unconverted(plan: &ModelPlan) -> bool {
571 plan.trunk_operations()
572 .contains(&OperationKind::HyperConnections)
573}
574
575pub fn gdn_dspark_compatible(plan: &ModelPlan) -> bool {
576 let operations = plan.trunk_operations();
577 operations.contains(&OperationKind::GatedDeltaNet)
578 && operations.contains(&OperationKind::FusedAttentionGate)
579}
580
581fn decode_graph_support(operation: OperationKind) -> OperationSupport {
582 let mut support = OperationSupport::none();
583 support.cuda_graph = matches!(
584 operation,
585 OperationKind::Embedding
586 | OperationKind::RmsNorm
587 | OperationKind::FullAttention
588 | OperationKind::GatedDeltaNet
589 | OperationKind::FusedAttentionGate
590 | OperationKind::DenseMlp
591 | OperationKind::MoeMlp
592 | OperationKind::SharedMlp
593 | OperationKind::SoftmaxRouter
594 | OperationKind::SigmoidRouter
595 | OperationKind::SiluActivation
596 | OperationKind::SerialResidual
597 | OperationKind::KvState
598 | OperationKind::RecurrentState
599 | OperationKind::LogitsMask
600 | OperationKind::OutputProjection
601 );
602 support
603}
604
605pub const DECODE_GRAPH: KernelManifest =
606 KernelManifest::new("decode-cuda-graph", decode_graph_support);
607
608fn pipeline_support(operation: OperationKind) -> OperationSupport {
641 let mut support = OperationSupport::none();
642 support.pipeline = matches!(
643 operation,
644 OperationKind::Embedding
645 | OperationKind::RmsNorm
646 | OperationKind::FullAttention
647 | OperationKind::SlidingWindowAttention
648 | OperationKind::SeparateAttentionGate
649 | OperationKind::DenseMlp
650 | OperationKind::MoeMlp
651 | OperationKind::SharedMlp
652 | OperationKind::SigmoidRouter
653 | OperationKind::SiluActivation
654 | OperationKind::SwiGluClampedActivation
655 | OperationKind::SerialResidual
656 | OperationKind::KvState
657 | OperationKind::SlidingKvState
658 | OperationKind::Mtp
659 | OperationKind::MtpFusion
660 | OperationKind::MtpHead
661 | OperationKind::LogitsMask
662 | OperationKind::OutputProjection
663 | OperationKind::PipelineBoundary
664 | OperationKind::KimiDeltaNet
666 | OperationKind::RecurrentState
667 | OperationKind::LatentMlaAttention
668 | OperationKind::SparseIndex
669 | OperationKind::LatentKvState
670 | OperationKind::HyperConnections
671 | OperationKind::SwiGluPreClampedActivation
672 );
673 support
674}
675
676pub const PIPELINE: KernelManifest =
677 KernelManifest::new("pipeline-state-transport", pipeline_support);
678
679fn decode_batch_support(operation: OperationKind) -> OperationSupport {
680 let mut support = OperationSupport::none();
681 support.batch = matches!(
682 operation,
683 OperationKind::Embedding
684 | OperationKind::RmsNorm
685 | OperationKind::FullAttention
686 | OperationKind::SlidingWindowAttention
687 | OperationKind::GatedDeltaNet
688 | OperationKind::FusedAttentionGate
689 | OperationKind::SeparateAttentionGate
690 | OperationKind::DenseMlp
691 | OperationKind::MoeMlp
692 | OperationKind::SharedMlp
693 | OperationKind::SoftmaxRouter
694 | OperationKind::SigmoidRouter
695 | OperationKind::SiluActivation
696 | OperationKind::GeluTanhActivation
697 | OperationKind::SwiGluClampedActivation
698 | OperationKind::SerialResidual
699 | OperationKind::GemmaResidual
700 | OperationKind::GemmaParallelMoeResidual
701 | OperationKind::KvState
702 | OperationKind::SlidingKvState
703 | OperationKind::RecurrentState
704 | OperationKind::LogitsSoftcap
705 | OperationKind::LogitsMask
706 | OperationKind::OutputProjection
707 );
708 support
709}
710
711pub const DECODE_BATCH: KernelManifest = KernelManifest::new("decode-batch", decode_batch_support);
712
713fn native_eager_support(operation: OperationKind) -> OperationSupport {
714 let mut support = OperationSupport::none();
715 support.batch = matches!(
716 operation,
717 OperationKind::Embedding
718 | OperationKind::RmsNorm
719 | OperationKind::FullAttention
720 | OperationKind::DenseMlp
721 | OperationKind::SiluActivation
722 | OperationKind::SerialResidual
723 | OperationKind::KvState
724 | OperationKind::LogitsSoftcap
725 | OperationKind::LogitsMask
726 | OperationKind::OutputProjection
727 );
728 support
729}
730
731pub const NATIVE_EAGER: KernelManifest = KernelManifest::new("native-eager", native_eager_support);
732
733fn mtp_spec_support(operation: OperationKind) -> OperationSupport {
734 let mut support = OperationSupport::none();
735 let common = matches!(
736 operation,
737 OperationKind::Embedding
738 | OperationKind::RmsNorm
739 | OperationKind::FullAttention
740 | OperationKind::SlidingWindowAttention
741 | OperationKind::GatedDeltaNet
742 | OperationKind::FusedAttentionGate
743 | OperationKind::SeparateAttentionGate
744 | OperationKind::DenseMlp
745 | OperationKind::MoeMlp
746 | OperationKind::SharedMlp
747 | OperationKind::SoftmaxRouter
748 | OperationKind::SigmoidRouter
749 | OperationKind::SiluActivation
750 | OperationKind::SwiGluClampedActivation
751 | OperationKind::SerialResidual
752 | OperationKind::KvState
753 | OperationKind::SlidingKvState
754 | OperationKind::RecurrentState
755 | OperationKind::LogitsMask
756 | OperationKind::OutputProjection
757 );
758 support.spec_draft = common
759 || matches!(
760 operation,
761 OperationKind::Mtp | OperationKind::MtpFusion | OperationKind::MtpHead
762 );
763 support.spec_verify = common;
764 support
765}
766
767pub const MTP_SPEC: KernelManifest = KernelManifest::new("mtp-spec", mtp_spec_support);
768
769fn glm5_spec_support(operation: OperationKind) -> OperationSupport {
790 let mut support = OperationSupport::none();
791 let common = matches!(
792 operation,
793 OperationKind::Embedding
794 | OperationKind::RmsNorm
795 | OperationKind::LatentMlaAttention
796 | OperationKind::SparseIndex
797 | OperationKind::SharedSparseIndex
798 | OperationKind::KimiDeltaNet
799 | OperationKind::RecurrentState
800 | OperationKind::LatentKvState
801 | OperationKind::MoeMlp
802 | OperationKind::SigmoidRouter
803 | OperationKind::SharedMlp
804 | OperationKind::SwiGluPreClampedActivation
805 | OperationKind::OutputProjection
806 );
807 support.spec_verify = common
808 || matches!(
809 operation,
810 OperationKind::HyperConnections | OperationKind::DenseMlp
811 );
812 support.spec_draft = common
813 || matches!(
814 operation,
815 OperationKind::Mtp
816 | OperationKind::MtpFusion
817 | OperationKind::MtpHead
818 | OperationKind::SerialResidual
819 );
820 support
821}
822
823pub const GLM5_SPEC: KernelManifest = KernelManifest::new("glm5-spec", glm5_spec_support);
824
825#[cfg(test)]
826mod tests {
827 use super::*;
828 use crate::config::{HfConfig, ModelConfig};
829
830 fn plan(json: &str) -> ModelPlan {
831 ModelPlan::compile(&ModelConfig::from_hf(&HfConfig::parse(json))).unwrap()
832 }
833
834 #[test]
835 fn manifest_reports_operation_blockers_instead_of_model_names() {
836 let dense = plan(
837 r#"{"model_type":"qwen3","num_hidden_layers":2,"hidden_size":64,
838 "num_attention_heads":2,"num_key_value_heads":1,"head_dim":32,
839 "intermediate_size":128,"vocab_size":16,"max_position_embeddings":128}"#,
840 );
841 assert!(CARRIED_PRIME.trunk_capabilities(&dense).batch.supported);
842 assert!(DECODE_BATCH.trunk_capabilities(&dense).batch.supported);
843 assert!(DECODE_GRAPH.trunk_capabilities(&dense).cuda_graph.supported);
844
845 let gemma = plan(
846 r#"{"model_type":"gemma4","num_hidden_layers":2,"hidden_size":64,
847 "num_attention_heads":2,"num_key_value_heads":1,"head_dim":32,
848 "global_head_dim":32,"intermediate_size":128,"vocab_size":16,
849 "max_position_embeddings":128,"sliding_window":64,
850 "layer_types":["sliding_attention","full_attention"],
851 "rope_parameters":{"full_attention":{"rope_theta":10000},
852 "sliding_attention":{"rope_theta":10000}}}"#,
853 );
854 let capability = CARRIED_PRIME.trunk_capabilities(&gemma).batch;
855 assert!(!capability.supported);
856 assert!(
857 capability
858 .blockers
859 .contains(&OperationKind::SlidingWindowAttention)
860 );
861 assert!(capability.blockers.contains(&OperationKind::GemmaResidual));
862 assert_eq!(decode_batch_program(&gemma), DecodeBatchProgram::Gemma);
863 assert!(!DECODE_GRAPH.trunk_capabilities(&gemma).cuda_graph.supported);
864
865 let mut sliding_gated_moe = plan(
866 r#"{"model_type":"qwen3_moe","num_hidden_layers":2,"hidden_size":64,
867 "num_attention_heads":2,"num_key_value_heads":1,"head_dim":32,
868 "intermediate_size":128,"vocab_size":16,"max_position_embeddings":128,
869 "num_experts":4,"num_experts_per_tok":2,"moe_intermediate_size":32}"#,
870 );
871 let crate::model_plan::AttentionPlan::Full(mut attention) =
872 sliding_gated_moe.layers[0].attention.clone()
873 else {
874 unreachable!()
875 };
876 attention.output_gate = crate::config::AttentionGateKind::SeparateHead;
877 sliding_gated_moe.layers[0].attention = crate::model_plan::AttentionPlan::SlidingWindow {
878 attention,
879 window: 64,
880 };
881 let crate::model_plan::MlpPlan::Moe(moe) = &mut sliding_gated_moe.layers[0].mlp else {
882 unreachable!()
883 };
884 moe.router = crate::model_plan::RouterPlan::Sigmoid {
885 normalize_selected: true,
886 scaling_factor: 1.0,
887 selection_bias: false,
888 };
889 assert_eq!(
890 decode_batch_program(&sliding_gated_moe),
891 DecodeBatchProgram::SlidingGatedMoe
892 );
893 assert!(
894 !DECODE_GRAPH
895 .trunk_capabilities(&sliding_gated_moe)
896 .cuda_graph
897 .supported
898 );
899 assert_eq!(decode_batch_program(&dense), DecodeBatchProgram::Generic);
900 assert_eq!(
901 MTP_SPEC.capabilities(&dense).speculative.blockers,
902 vec![OperationKind::DraftPlan]
903 );
904
905 let qwen35 = crate::model_packs::by_alias("qwen35")
906 .unwrap()
907 .compile_tiny_plan()
908 .unwrap();
909 assert!(MTP_SPEC.capabilities(&qwen35).speculative.supported);
910
911 let dsv4 = crate::model_packs::by_alias("deepseek_v4_dspark")
912 .unwrap()
913 .compile_tiny_plan()
914 .unwrap();
915 let dsv4_batch = DECODE_BATCH.trunk_capabilities(&dsv4).batch;
916 assert!(!dsv4_batch.supported);
917 assert!(
918 dsv4_batch
919 .blockers
920 .contains(&OperationKind::CompressedMlaAttention)
921 );
922 assert!(!MTP_SPEC.capabilities(&dsv4).speculative.supported);
923
924 let root = std::env::temp_dir().join(format!(
925 "memra-plan-backend-external-draft-{}",
926 std::process::id()
927 ));
928 std::fs::create_dir_all(&root).unwrap();
929 let trunk_path = root.join("trunk.gguf");
930 let draft_path = root.join("draft.gguf");
931 crate::micro_gguf::write_step35_meta_only(&trunk_path).unwrap();
932 crate::micro_gguf::write_step35_mtp_meta_only(&draft_path).unwrap();
933 let trunk_cfg = ModelConfig::from_gguf(&crate::GgufFile::open(&trunk_path).unwrap());
934 let draft_cfg = ModelConfig::from_gguf(&crate::GgufFile::open(&draft_path).unwrap());
935 let mut external = crate::model_packs::for_config(&trunk_cfg)
936 .unwrap()
937 .compile_plan(&trunk_cfg)
938 .unwrap();
939 let draft = crate::model_packs::for_config(&draft_cfg)
940 .unwrap()
941 .compile_plan(&draft_cfg)
942 .unwrap();
943 assert_eq!(
944 external.draft_source,
945 crate::model_plan::DraftSourcePlan::ExternalArtifact
946 );
947 external.attach_external_draft(&draft).unwrap();
948 assert!(MTP_SPEC.capabilities(&external).speculative.supported);
949 std::fs::remove_dir_all(root).unwrap();
950 }
951
952 #[test]
953 fn rewrite_receipts_bind_plan_program_binary_and_outputs() {
954 let dense = plan(
955 r#"{"model_type":"qwen3","num_hidden_layers":2,"hidden_size":64,
956 "num_attention_heads":2,"num_key_value_heads":1,"head_dim":32,
957 "intermediate_size":128,"vocab_size":16,"max_position_embeddings":128}"#,
958 );
959 let rewrites = execution_rewrites(&dense);
960 let batch = rewrites
961 .iter()
962 .find(|rewrite| rewrite.surface == RewriteSurface::DecodeBatch)
963 .unwrap();
964 assert!(batch.eligible());
965 let spec = rewrites
966 .iter()
967 .find(|rewrite| rewrite.surface == RewriteSurface::MtpSpec)
968 .unwrap();
969 assert_eq!(spec.blockers, vec![OperationKind::DraftPlan]);
970
971 let policy = RewriteParityPolicy {
972 max_abs: 0.01,
973 max_rel: 0.01,
974 require_argmax: true,
975 };
976 let implementation = "00".repeat(32);
977 let artifact_lock = b"format_version=2\nfamily=qwen3\n";
978 let receipt = batch
979 .verify_logits(
980 &implementation,
981 &[0.0, 1.0, -1.0],
982 &[0.0, 1.001, -1.001],
983 policy,
984 )
985 .unwrap()
986 .bind_artifact_lock(artifact_lock);
987 assert!(receipt.passed);
988 receipt.validate_for(batch).unwrap();
989 let tsv = receipt.to_tsv();
990 assert!(tsv.contains("rewrite\tdecode-batch.v1"));
991 assert!(tsv.contains(&format!("plan_sha256\t{}", batch.plan_sha256)));
992 assert!(tsv.contains(&format!("implementation_sha256\t{implementation}")));
993
994 let root = std::env::temp_dir().join(format!(
995 "memra-rewrite-qualification-{}",
996 std::process::id()
997 ));
998 let receipts = root.join("rewrite-receipts");
999 std::fs::create_dir_all(&receipts).unwrap();
1000 std::fs::write(root.join("artifact.lock"), artifact_lock).unwrap();
1001 std::fs::write(receipts.join("decode-batch.v1.tsv"), &tsv).unwrap();
1002 std::fs::write(
1003 root.join("rewrite-receipts.tsv"),
1004 format!(
1005 "rewrite\tplan_sha256\treceipt_sha256\tstatus\n\
1006 decode-batch.v1\t{}\t{}\tpassed\n",
1007 batch.plan_sha256,
1008 hex_sha256(tsv.as_bytes())
1009 ),
1010 )
1011 .unwrap();
1012 let qualifications = RewriteQualifications::load(&root, &dense).unwrap();
1013 assert!(qualifications.allows(RewriteSurface::DecodeBatch));
1014 assert!(!qualifications.allows(RewriteSurface::DecodeGraph));
1015 std::fs::write(receipts.join("decode-batch.v1.tsv"), "tampered").unwrap();
1016 assert!(RewriteQualifications::load(&root, &dense).is_err());
1017 std::fs::remove_dir_all(root).unwrap();
1018
1019 let failed = batch
1020 .verify_logits(
1021 &implementation,
1022 &[0.0, 1.0, -1.0],
1023 &[2.0, 1.0, -1.0],
1024 policy,
1025 )
1026 .unwrap();
1027 assert!(!failed.passed);
1028 assert!(failed.validate_for(batch).is_err());
1029
1030 let other = plan(
1031 r#"{"model_type":"qwen3","num_hidden_layers":3,"hidden_size":64,
1032 "num_attention_heads":2,"num_key_value_heads":1,"head_dim":32,
1033 "intermediate_size":128,"vocab_size":16,"max_position_embeddings":128}"#,
1034 );
1035 let other_batch = execution_rewrites(&other)
1036 .into_iter()
1037 .find(|rewrite| rewrite.surface == RewriteSurface::DecodeBatch)
1038 .unwrap();
1039 assert!(receipt.validate_for(&other_batch).is_err());
1040
1041 let qwen35 = crate::model_packs::by_alias("qwen35")
1042 .unwrap()
1043 .compile_tiny_plan()
1044 .unwrap();
1045 let qwen35_spec = execution_rewrites(&qwen35)
1046 .into_iter()
1047 .find(|rewrite| rewrite.surface == RewriteSurface::MtpSpec)
1048 .unwrap();
1049 assert!(qwen35_spec.eligible());
1050 }
1051}
1052
1053#[cfg(test)]
1058mod glm5_spec_class_matrix {
1059 use super::*;
1060 use crate::model_plan::OperationKind;
1061
1062 fn glm5_plan(nextn: u32) -> crate::model_plan::ModelPlan {
1068 let json = format!(
1069 r#"{{
1070 "model_type": "glm5_next_text", "num_hidden_layers": 4,
1071 "num_nextn_predict_layers": {nextn},
1072 "hidden_size": 128, "intermediate_size": 64, "vocab_size": 32,
1073 "max_position_embeddings": 512, "rms_norm_eps": 1e-05, "hidden_act": "silu",
1074 "swiglu_limit": 10.0, "tie_word_embeddings": true, "hc_mult": 4, "hc_eps": 1e-06,
1075 "hc_sinkhorn_iters": 20, "mhc": true,
1076 "layer_types": ["linear_attention", "deepseek_sparse_attention",
1077 "linear_attention", "deepseek_sparse_attention"],
1078 "mlp_layer_types": ["dense", "sparse", "sparse", "sparse"],
1079 "first_k_dense_replace": 1, "indexer_types": ["full", "full", "full", "full"],
1080 "linear_attn_config": {{"num_heads": 1, "head_dim": 128, "short_conv_kernel_size": 4,
1081 "gate_lower_bound": -5.0, "kda_layers": [0, 2], "full_attn_layers": [1, 3]}},
1082 "num_attention_heads": 2, "num_key_value_heads": 2, "q_lora_rank": 16,
1083 "kv_lora_rank": 16, "qk_head_dim": 16, "qk_nope_head_dim": 16, "qk_rope_head_dim": 0,
1084 "v_head_dim": 16, "mla_use_nope": true, "index_n_heads": 1, "index_head_dim": 8,
1085 "index_topk": 8, "index_kpool": 4, "index_kpool_always_select_tail": true,
1086 "index_kpool_compress": true, "indexer_rope_interleave": true,
1087 "index_share_for_mtp_iteration": true, "n_routed_experts": 4, "num_experts_per_tok": 2,
1088 "moe_intermediate_size": 64, "n_shared_experts": 1, "scoring_func": "sigmoid",
1089 "topk_method": "noaux_tc", "routed_scaling_factor": 2.5, "norm_topk_prob": true,
1090 "n_group": 1, "topk_group": 1, "head_dim": 0, "attention_bias": false,
1091 "moe_router_dtype": "float32", "dtype": "bfloat16"
1092 }}"#
1093 );
1094 let config = crate::config::ModelConfig::from_hf(&crate::config::HfConfig::parse(&json));
1095 crate::model_packs::for_config(&config)
1096 .unwrap()
1097 .compile_plan(&config)
1098 .unwrap()
1099 }
1100
1101 #[test]
1102 fn glm5_with_mtp_block_is_supported_and_the_rewrite_row_is_eligible() {
1103 let plan = glm5_plan(1);
1104 let capability = GLM5_SPEC.capabilities(&plan).speculative;
1105 assert!(
1106 capability.supported,
1107 "glm5_next + MTP block must be the supported class; blockers: {:?}",
1108 capability.blockers
1109 );
1110 let rewrite = execution_rewrites(&plan)
1111 .into_iter()
1112 .find(|rewrite| rewrite.surface == RewriteSurface::Glm5Spec)
1113 .unwrap();
1114 assert!(
1115 rewrite.eligible(),
1116 "glm5-spec.v1 must be eligible for this plan"
1117 );
1118 assert_eq!(rewrite.id, "glm5-spec.v1");
1122 }
1123
1124 #[test]
1125 fn glm5_without_an_mtp_block_fails_closed_on_the_draft_plan() {
1126 let plan = glm5_plan(0);
1127 let capability = GLM5_SPEC.capabilities(&plan).speculative;
1128 assert!(!capability.supported);
1129 assert_eq!(
1130 capability.blockers,
1131 vec![OperationKind::DraftPlan],
1132 "no NextN block = no draft program; the blocker must name it"
1133 );
1134 }
1135
1136 #[test]
1137 fn the_two_spec_programs_never_both_claim_one_plan() {
1138 let glm5 = glm5_plan(1);
1142 let mtp_spec = MTP_SPEC.capabilities(&glm5).speculative;
1143 assert!(!mtp_spec.supported);
1144 assert!(
1145 mtp_spec.blockers.contains(&OperationKind::HyperConnections),
1146 "the hc topology must be among MTP_SPEC's named blockers: {:?}",
1147 mtp_spec.blockers
1148 );
1149
1150 let qwen35 = crate::model_packs::by_alias("qwen35")
1153 .unwrap()
1154 .compile_tiny_plan()
1155 .unwrap();
1156 assert!(MTP_SPEC.capabilities(&qwen35).speculative.supported);
1157 let qwen_glm5 = GLM5_SPEC.capabilities(&qwen35).speculative;
1158 assert!(!qwen_glm5.supported);
1159 assert!(
1160 qwen_glm5.blockers.contains(&OperationKind::SerialResidual),
1161 "a serial-residual trunk must block the hc walk program: {:?}",
1162 qwen_glm5.blockers
1163 );
1164 }
1165
1166 #[test]
1167 fn foreign_state_classes_fail_closed_under_glm5_spec() {
1168 let dsv4 = crate::model_packs::by_alias("deepseek_v4_dspark")
1170 .unwrap()
1171 .compile_tiny_plan()
1172 .unwrap();
1173 let dsv4_cap = GLM5_SPEC.capabilities(&dsv4).speculative;
1174 assert!(!dsv4_cap.supported);
1175 assert!(
1176 dsv4_cap
1177 .blockers
1178 .contains(&OperationKind::CompressedMlaAttention)
1179 );
1180
1181 let dense = crate::model_packs::by_alias("qwen3")
1183 .unwrap()
1184 .compile_tiny_plan()
1185 .unwrap();
1186 assert!(!GLM5_SPEC.capabilities(&dense).speculative.supported);
1187 }
1188}