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 Pipeline,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct RewriteQualifications {
53 pub plan_sha256: String,
54 passed: BTreeSet<RewriteSurface>,
55}
56
57impl RewriteQualifications {
58 pub fn load(bundle: &Path, plan: &ModelPlan) -> Result<Self, String> {
59 let expected = execution_rewrites(plan);
60 let artifact_lock = std::fs::read(bundle.join("artifact.lock"))
61 .map_err(|error| format!("read artifact.lock: {error}"))?;
62 let artifact_lock_sha256 = hex_sha256(&artifact_lock);
63 let index = std::fs::read_to_string(bundle.join("rewrite-receipts.tsv"))
64 .map_err(|error| format!("read rewrite receipt index: {error}"))?;
65 let mut passed = BTreeSet::new();
66 for line in index.lines().skip(1) {
67 let columns: Vec<_> = line.split('\t').collect();
68 if columns.len() != 4 || columns[3] != "passed" {
69 return Err(format!("malformed rewrite receipt index row {line:?}"));
70 }
71 let rewrite = expected
72 .iter()
73 .find(|rewrite| rewrite.id == columns[0])
74 .ok_or_else(|| format!("receipt names unknown rewrite {}", columns[0]))?;
75 if !rewrite.eligible() || columns[1] != rewrite.plan_sha256 {
76 return Err(format!(
77 "receipt {} is not eligible for plan {}",
78 rewrite.id, rewrite.plan_sha256
79 ));
80 }
81 let receipt_path = bundle
82 .join("rewrite-receipts")
83 .join(format!("{}.tsv", rewrite.id));
84 let receipt = std::fs::read(&receipt_path)
85 .map_err(|error| format!("read {}: {error}", receipt_path.display()))?;
86 if hex_sha256(&receipt) != columns[2] {
87 return Err(format!("rewrite receipt hash mismatch for {}", rewrite.id));
88 }
89 let text = std::str::from_utf8(&receipt)
90 .map_err(|error| format!("rewrite receipt is not UTF-8: {error}"))?;
91 for (key, value) in [
92 ("status", "passed"),
93 ("rewrite", rewrite.id),
94 ("surface", rewrite.surface.as_str()),
95 ("implementation", rewrite.implementation),
96 ("plan_sha256", rewrite.plan_sha256.as_str()),
97 ("artifact_lock_sha256", artifact_lock_sha256.as_str()),
98 ("first_violation", "none"),
99 ] {
100 if !text.lines().any(|line| line == format!("{key}\t{value}")) {
101 return Err(format!(
102 "rewrite receipt {} does not bind {key}={value}",
103 rewrite.id
104 ));
105 }
106 }
107 passed.insert(rewrite.surface);
108 }
109 Ok(Self {
110 plan_sha256: plan_sha256(plan),
111 passed,
112 })
113 }
114
115 pub fn allows(&self, surface: RewriteSurface) -> bool {
116 self.passed.contains(&surface)
117 }
118
119 pub fn all_eligible(&self, plan: &ModelPlan) -> bool {
120 execution_rewrites(plan)
121 .into_iter()
122 .filter(ExecutionRewrite::eligible)
123 .all(|rewrite| self.allows(rewrite.surface))
124 }
125}
126
127impl RewriteSurface {
128 pub const fn as_str(self) -> &'static str {
129 match self {
130 Self::CarriedPrime => "carried-prime",
131 Self::DecodeEager => "decode-eager",
132 Self::DecodeBatch => "decode-batch",
133 Self::DecodeGraph => "decode-graph",
134 Self::MtpSpec => "mtp-spec",
135 Self::Pipeline => "pipeline",
136 }
137 }
138}
139
140#[derive(Debug, Clone, PartialEq, Eq)]
141pub struct ExecutionRewrite {
142 pub id: &'static str,
143 pub surface: RewriteSurface,
144 pub implementation: &'static str,
145 pub plan_sha256: String,
146 pub canonical_operations: Vec<OperationKind>,
147 pub blockers: Vec<OperationKind>,
148}
149
150impl ExecutionRewrite {
151 pub fn eligible(&self) -> bool {
152 self.blockers.is_empty()
153 }
154
155 pub fn verify_logits(
156 &self,
157 implementation_sha256: &str,
158 reference: &[f32],
159 candidate: &[f32],
160 policy: RewriteParityPolicy,
161 ) -> Result<RewriteParityReceipt, String> {
162 if !self.eligible() {
163 return Err(format!(
164 "rewrite {} is blocked by {:?}",
165 self.id, self.blockers
166 ));
167 }
168 if !is_sha256(implementation_sha256) {
169 return Err("rewrite implementation identity must be a lowercase SHA-256".into());
170 }
171 if reference.len() != candidate.len() || reference.is_empty() {
172 return Err(format!(
173 "rewrite parity requires equal non-empty streams (reference={} candidate={})",
174 reference.len(),
175 candidate.len()
176 ));
177 }
178 let mut max_abs = 0.0f32;
179 let mut max_rel = 0.0f32;
180 let mut first_violation = None;
181 for (index, (&expected, &actual)) in reference.iter().zip(candidate).enumerate() {
182 if !expected.is_finite() || !actual.is_finite() {
183 return Err(format!("rewrite parity has a non-finite value at {index}"));
184 }
185 let absolute = (expected - actual).abs();
186 let relative = absolute / expected.abs().max(1e-6);
187 max_abs = max_abs.max(absolute);
188 max_rel = max_rel.max(relative);
189 let allowed = policy.max_abs + policy.max_rel * expected.abs();
190 if absolute > allowed && first_violation.is_none() {
191 first_violation = Some(index);
192 }
193 }
194 let reference_argmax = stable_argmax(reference);
195 let candidate_argmax = stable_argmax(candidate);
196 let passed = first_violation.is_none()
197 && (!policy.require_argmax || reference_argmax == candidate_argmax);
198 Ok(RewriteParityReceipt {
199 rewrite_id: self.id,
200 surface: self.surface,
201 implementation: self.implementation,
202 implementation_sha256: implementation_sha256.to_string(),
203 plan_sha256: self.plan_sha256.clone(),
204 artifact_lock_sha256: None,
205 reference_sha256: f32_stream_sha256(reference),
206 candidate_sha256: f32_stream_sha256(candidate),
207 value_kind: RewriteValueKind::LogitsF32,
208 values: reference.len(),
209 max_abs,
210 max_rel,
211 reference_argmax,
212 candidate_argmax,
213 policy,
214 passed,
215 first_violation,
216 })
217 }
218
219 pub fn verify_tokens(
220 &self,
221 implementation_sha256: &str,
222 reference: &[u32],
223 candidate: &[u32],
224 ) -> Result<RewriteParityReceipt, String> {
225 if !self.eligible() {
226 return Err(format!(
227 "rewrite {} is blocked by {:?}",
228 self.id, self.blockers
229 ));
230 }
231 if !is_sha256(implementation_sha256) {
232 return Err("rewrite implementation identity must be a lowercase SHA-256".into());
233 }
234 if reference.len() != candidate.len() || reference.is_empty() {
235 return Err(format!(
236 "rewrite parity requires equal non-empty token streams (reference={} candidate={})",
237 reference.len(),
238 candidate.len()
239 ));
240 }
241 let first_violation = reference
242 .iter()
243 .zip(candidate)
244 .position(|(expected, actual)| expected != actual);
245 let max_abs = reference
246 .iter()
247 .zip(candidate)
248 .map(|(&expected, &actual)| expected.abs_diff(actual) as f32)
249 .fold(0.0f32, f32::max);
250 Ok(RewriteParityReceipt {
251 rewrite_id: self.id,
252 surface: self.surface,
253 implementation: self.implementation,
254 implementation_sha256: implementation_sha256.to_string(),
255 plan_sha256: self.plan_sha256.clone(),
256 artifact_lock_sha256: None,
257 reference_sha256: u32_stream_sha256(reference),
258 candidate_sha256: u32_stream_sha256(candidate),
259 value_kind: RewriteValueKind::TokenIdsU32,
260 values: reference.len(),
261 max_abs,
262 max_rel: 0.0,
263 reference_argmax: 0,
264 candidate_argmax: 0,
265 policy: RewriteParityPolicy {
266 max_abs: 0.0,
267 max_rel: 0.0,
268 require_argmax: false,
269 },
270 passed: first_violation.is_none(),
271 first_violation,
272 })
273 }
274}
275
276#[derive(Debug, Clone, Copy, PartialEq)]
277pub struct RewriteParityPolicy {
278 pub max_abs: f32,
279 pub max_rel: f32,
280 pub require_argmax: bool,
281}
282
283#[derive(Debug, Clone, PartialEq)]
284pub struct RewriteParityReceipt {
285 pub rewrite_id: &'static str,
286 pub surface: RewriteSurface,
287 pub implementation: &'static str,
288 pub implementation_sha256: String,
289 pub plan_sha256: String,
290 pub artifact_lock_sha256: Option<String>,
291 pub reference_sha256: String,
292 pub candidate_sha256: String,
293 pub value_kind: RewriteValueKind,
294 pub values: usize,
295 pub max_abs: f32,
296 pub max_rel: f32,
297 pub reference_argmax: usize,
298 pub candidate_argmax: usize,
299 pub policy: RewriteParityPolicy,
300 pub passed: bool,
301 pub first_violation: Option<usize>,
302}
303
304#[derive(Debug, Clone, Copy, PartialEq, Eq)]
305pub enum RewriteValueKind {
306 LogitsF32,
307 TokenIdsU32,
308}
309
310impl RewriteValueKind {
311 pub const fn as_str(self) -> &'static str {
312 match self {
313 Self::LogitsF32 => "logits-f32",
314 Self::TokenIdsU32 => "token-ids-u32",
315 }
316 }
317}
318
319impl RewriteParityReceipt {
320 pub fn bind_artifact_lock(mut self, artifact_lock: &[u8]) -> Self {
321 self.artifact_lock_sha256 = Some(hex_sha256(artifact_lock));
322 self
323 }
324
325 pub fn validate_for(&self, rewrite: &ExecutionRewrite) -> Result<(), String> {
326 if self.rewrite_id != rewrite.id
327 || self.surface != rewrite.surface
328 || self.implementation != rewrite.implementation
329 || self.plan_sha256 != rewrite.plan_sha256
330 {
331 return Err(format!(
332 "rewrite receipt identity does not match {} for plan {}",
333 rewrite.id, rewrite.plan_sha256
334 ));
335 }
336 if !rewrite.eligible() {
337 return Err(format!("rewrite {} is no longer eligible", rewrite.id));
338 }
339 if !self.passed {
340 return Err(format!("rewrite {} parity receipt failed", rewrite.id));
341 }
342 Ok(())
343 }
344
345 pub fn to_tsv(&self) -> String {
346 let mut output = String::new();
347 writeln!(output, "format\tmemra-rewrite-parity-v1").unwrap();
348 writeln!(
349 output,
350 "status\t{}",
351 if self.passed { "passed" } else { "failed" }
352 )
353 .unwrap();
354 writeln!(output, "rewrite\t{}", self.rewrite_id).unwrap();
355 writeln!(output, "surface\t{}", self.surface.as_str()).unwrap();
356 writeln!(output, "implementation\t{}", self.implementation).unwrap();
357 writeln!(
358 output,
359 "implementation_sha256\t{}",
360 self.implementation_sha256
361 )
362 .unwrap();
363 writeln!(output, "plan_sha256\t{}", self.plan_sha256).unwrap();
364 if let Some(hash) = self.artifact_lock_sha256.as_ref() {
365 writeln!(output, "artifact_lock_sha256\t{hash}").unwrap();
366 }
367 writeln!(output, "reference_sha256\t{}", self.reference_sha256).unwrap();
368 writeln!(output, "candidate_sha256\t{}", self.candidate_sha256).unwrap();
369 writeln!(output, "value_kind\t{}", self.value_kind.as_str()).unwrap();
370 writeln!(output, "values\t{}", self.values).unwrap();
371 writeln!(output, "max_abs\t{}", self.max_abs).unwrap();
372 writeln!(output, "max_rel\t{}", self.max_rel).unwrap();
373 writeln!(output, "reference_argmax\t{}", self.reference_argmax).unwrap();
374 writeln!(output, "candidate_argmax\t{}", self.candidate_argmax).unwrap();
375 writeln!(output, "atol\t{}", self.policy.max_abs).unwrap();
376 writeln!(output, "rtol\t{}", self.policy.max_rel).unwrap();
377 writeln!(output, "require_argmax\t{}", self.policy.require_argmax).unwrap();
378 writeln!(
379 output,
380 "first_violation\t{}",
381 self.first_violation
382 .map_or_else(|| "none".to_string(), |index| index.to_string())
383 )
384 .unwrap();
385 output
386 }
387}
388
389pub fn execution_rewrites(plan: &ModelPlan) -> Vec<ExecutionRewrite> {
390 let plan_sha256 = plan_sha256(plan);
391 let trunk = plan.trunk_operations();
392 let mut spec_operations = plan
393 .draft_operations()
394 .unwrap_or_else(|| vec![OperationKind::DraftPlan]);
395 spec_operations.extend(plan.trunk_operations());
396 let selections = [
397 (
398 "carried-prime.v1",
399 RewriteSurface::CarriedPrime,
400 CARRIED_PRIME,
401 trunk.clone(),
402 CARRIED_PRIME.trunk_capabilities(plan).batch,
403 ),
404 (
405 "decode-eager.v1",
406 RewriteSurface::DecodeEager,
407 NATIVE_EAGER,
408 trunk.clone(),
409 NATIVE_EAGER.trunk_capabilities(plan).batch,
410 ),
411 (
412 "decode-batch.v1",
413 RewriteSurface::DecodeBatch,
414 DECODE_BATCH,
415 trunk.clone(),
416 DECODE_BATCH.trunk_capabilities(plan).batch,
417 ),
418 (
419 "decode-graph.v1",
420 RewriteSurface::DecodeGraph,
421 DECODE_GRAPH,
422 trunk.clone(),
423 DECODE_GRAPH.trunk_capabilities(plan).cuda_graph,
424 ),
425 (
426 "mtp-spec.v1",
427 RewriteSurface::MtpSpec,
428 MTP_SPEC,
429 spec_operations,
430 MTP_SPEC.capabilities(plan).speculative,
431 ),
432 (
433 "pipeline.v1",
434 RewriteSurface::Pipeline,
435 PIPELINE,
436 trunk,
437 PIPELINE.capabilities(plan).pipeline,
438 ),
439 ];
440 selections
441 .into_iter()
442 .map(
443 |(id, surface, manifest, canonical_operations, capability)| ExecutionRewrite {
444 id,
445 surface,
446 implementation: manifest.name,
447 plan_sha256: plan_sha256.clone(),
448 canonical_operations,
449 blockers: capability.blockers,
450 },
451 )
452 .collect()
453}
454
455fn plan_sha256(plan: &ModelPlan) -> String {
456 hex_sha256(format!("{plan:#?}\n").as_bytes())
457}
458
459fn f32_stream_sha256(values: &[f32]) -> String {
460 let mut bytes = Vec::with_capacity(values.len() * 4);
461 for value in values {
462 bytes.extend_from_slice(&value.to_bits().to_le_bytes());
463 }
464 hex_sha256(&bytes)
465}
466
467fn u32_stream_sha256(values: &[u32]) -> String {
468 let mut bytes = Vec::with_capacity(values.len() * 4);
469 for value in values {
470 bytes.extend_from_slice(&value.to_le_bytes());
471 }
472 hex_sha256(&bytes)
473}
474
475fn hex_sha256(bytes: &[u8]) -> String {
476 let digest = Sha256::digest(bytes);
477 digest.iter().map(|byte| format!("{byte:02x}")).collect()
478}
479
480fn is_sha256(value: &str) -> bool {
481 value.len() == 64
482 && value
483 .bytes()
484 .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
485}
486
487fn stable_argmax(values: &[f32]) -> usize {
488 values
489 .iter()
490 .enumerate()
491 .max_by(|(left_index, left), (right_index, right)| {
492 left.total_cmp(right)
493 .then_with(|| right_index.cmp(left_index))
494 })
495 .map(|(index, _)| index)
496 .unwrap_or(0)
497}
498
499fn carried_prime_support(operation: OperationKind) -> OperationSupport {
500 let mut support = OperationSupport::none();
501 support.batch = matches!(
502 operation,
503 OperationKind::Embedding
504 | OperationKind::RmsNorm
505 | OperationKind::FullAttention
506 | OperationKind::GatedDeltaNet
507 | OperationKind::FusedAttentionGate
508 | OperationKind::DenseMlp
509 | OperationKind::SiluActivation
510 | OperationKind::SerialResidual
511 | OperationKind::KvState
512 | OperationKind::RecurrentState
513 | OperationKind::LogitsMask
514 | OperationKind::OutputProjection
515 );
516 support
517}
518
519pub const CARRIED_PRIME: KernelManifest =
520 KernelManifest::new("carried-prime-batch", carried_prime_support);
521
522#[derive(Debug, Clone, Copy, PartialEq, Eq)]
523pub enum DecodeBatchProgram {
524 Generic,
525 SlidingGatedMoe,
526 Gemma,
527}
528
529pub fn decode_batch_program(plan: &ModelPlan) -> DecodeBatchProgram {
530 let operations = plan.trunk_operations();
531 if operations.contains(&OperationKind::GemmaResidual)
532 || operations.contains(&OperationKind::GemmaParallelMoeResidual)
533 {
534 DecodeBatchProgram::Gemma
535 } else if operations.contains(&OperationKind::SlidingWindowAttention)
536 && operations.contains(&OperationKind::SeparateAttentionGate)
537 && operations.contains(&OperationKind::MoeMlp)
538 && operations.contains(&OperationKind::SigmoidRouter)
539 {
540 DecodeBatchProgram::SlidingGatedMoe
541 } else {
542 DecodeBatchProgram::Generic
543 }
544}
545
546pub fn gdn_dspark_compatible(plan: &ModelPlan) -> bool {
547 let operations = plan.trunk_operations();
548 operations.contains(&OperationKind::GatedDeltaNet)
549 && operations.contains(&OperationKind::FusedAttentionGate)
550}
551
552fn decode_graph_support(operation: OperationKind) -> OperationSupport {
553 let mut support = OperationSupport::none();
554 support.cuda_graph = matches!(
555 operation,
556 OperationKind::Embedding
557 | OperationKind::RmsNorm
558 | OperationKind::FullAttention
559 | OperationKind::GatedDeltaNet
560 | OperationKind::FusedAttentionGate
561 | OperationKind::DenseMlp
562 | OperationKind::MoeMlp
563 | OperationKind::SharedMlp
564 | OperationKind::SoftmaxRouter
565 | OperationKind::SigmoidRouter
566 | OperationKind::SiluActivation
567 | OperationKind::SerialResidual
568 | OperationKind::KvState
569 | OperationKind::RecurrentState
570 | OperationKind::LogitsMask
571 | OperationKind::OutputProjection
572 );
573 support
574}
575
576pub const DECODE_GRAPH: KernelManifest =
577 KernelManifest::new("decode-cuda-graph", decode_graph_support);
578
579fn pipeline_support(operation: OperationKind) -> OperationSupport {
580 let mut support = OperationSupport::none();
581 support.pipeline = matches!(
582 operation,
583 OperationKind::Embedding
584 | OperationKind::RmsNorm
585 | OperationKind::FullAttention
586 | OperationKind::SlidingWindowAttention
587 | OperationKind::SeparateAttentionGate
588 | OperationKind::DenseMlp
589 | OperationKind::MoeMlp
590 | OperationKind::SharedMlp
591 | OperationKind::SigmoidRouter
592 | OperationKind::SiluActivation
593 | OperationKind::SwiGluClampedActivation
594 | OperationKind::SerialResidual
595 | OperationKind::KvState
596 | OperationKind::SlidingKvState
597 | OperationKind::Mtp
598 | OperationKind::MtpFusion
599 | OperationKind::MtpHead
600 | OperationKind::LogitsMask
601 | OperationKind::OutputProjection
602 | OperationKind::PipelineBoundary
603 );
604 support
605}
606
607pub const PIPELINE: KernelManifest =
608 KernelManifest::new("pipeline-state-transport", pipeline_support);
609
610fn decode_batch_support(operation: OperationKind) -> OperationSupport {
611 let mut support = OperationSupport::none();
612 support.batch = matches!(
613 operation,
614 OperationKind::Embedding
615 | OperationKind::RmsNorm
616 | OperationKind::FullAttention
617 | OperationKind::SlidingWindowAttention
618 | OperationKind::GatedDeltaNet
619 | OperationKind::FusedAttentionGate
620 | OperationKind::SeparateAttentionGate
621 | OperationKind::DenseMlp
622 | OperationKind::MoeMlp
623 | OperationKind::SharedMlp
624 | OperationKind::SoftmaxRouter
625 | OperationKind::SigmoidRouter
626 | OperationKind::SiluActivation
627 | OperationKind::GeluTanhActivation
628 | OperationKind::SwiGluClampedActivation
629 | OperationKind::SerialResidual
630 | OperationKind::GemmaResidual
631 | OperationKind::GemmaParallelMoeResidual
632 | OperationKind::KvState
633 | OperationKind::SlidingKvState
634 | OperationKind::RecurrentState
635 | OperationKind::LogitsSoftcap
636 | OperationKind::LogitsMask
637 | OperationKind::OutputProjection
638 );
639 support
640}
641
642pub const DECODE_BATCH: KernelManifest = KernelManifest::new("decode-batch", decode_batch_support);
643
644fn native_eager_support(operation: OperationKind) -> OperationSupport {
645 let mut support = OperationSupport::none();
646 support.batch = matches!(
647 operation,
648 OperationKind::Embedding
649 | OperationKind::RmsNorm
650 | OperationKind::FullAttention
651 | OperationKind::DenseMlp
652 | OperationKind::SiluActivation
653 | OperationKind::SerialResidual
654 | OperationKind::KvState
655 | OperationKind::LogitsSoftcap
656 | OperationKind::LogitsMask
657 | OperationKind::OutputProjection
658 );
659 support
660}
661
662pub const NATIVE_EAGER: KernelManifest = KernelManifest::new("native-eager", native_eager_support);
663
664fn mtp_spec_support(operation: OperationKind) -> OperationSupport {
665 let mut support = OperationSupport::none();
666 let common = matches!(
667 operation,
668 OperationKind::Embedding
669 | OperationKind::RmsNorm
670 | OperationKind::FullAttention
671 | OperationKind::SlidingWindowAttention
672 | OperationKind::GatedDeltaNet
673 | OperationKind::FusedAttentionGate
674 | OperationKind::SeparateAttentionGate
675 | OperationKind::DenseMlp
676 | OperationKind::MoeMlp
677 | OperationKind::SharedMlp
678 | OperationKind::SoftmaxRouter
679 | OperationKind::SigmoidRouter
680 | OperationKind::SiluActivation
681 | OperationKind::SwiGluClampedActivation
682 | OperationKind::SerialResidual
683 | OperationKind::KvState
684 | OperationKind::SlidingKvState
685 | OperationKind::RecurrentState
686 | OperationKind::LogitsMask
687 | OperationKind::OutputProjection
688 );
689 support.spec_draft = common
690 || matches!(
691 operation,
692 OperationKind::Mtp | OperationKind::MtpFusion | OperationKind::MtpHead
693 );
694 support.spec_verify = common;
695 support
696}
697
698pub const MTP_SPEC: KernelManifest = KernelManifest::new("mtp-spec", mtp_spec_support);
699
700#[cfg(test)]
701mod tests {
702 use super::*;
703 use crate::config::{HfConfig, ModelConfig};
704
705 fn plan(json: &str) -> ModelPlan {
706 ModelPlan::compile(&ModelConfig::from_hf(&HfConfig::parse(json))).unwrap()
707 }
708
709 #[test]
710 fn manifest_reports_operation_blockers_instead_of_model_names() {
711 let dense = plan(
712 r#"{"model_type":"qwen3","num_hidden_layers":2,"hidden_size":64,
713 "num_attention_heads":2,"num_key_value_heads":1,"head_dim":32,
714 "intermediate_size":128,"vocab_size":16,"max_position_embeddings":128}"#,
715 );
716 assert!(CARRIED_PRIME.trunk_capabilities(&dense).batch.supported);
717 assert!(DECODE_BATCH.trunk_capabilities(&dense).batch.supported);
718 assert!(DECODE_GRAPH.trunk_capabilities(&dense).cuda_graph.supported);
719
720 let gemma = plan(
721 r#"{"model_type":"gemma4","num_hidden_layers":2,"hidden_size":64,
722 "num_attention_heads":2,"num_key_value_heads":1,"head_dim":32,
723 "global_head_dim":32,"intermediate_size":128,"vocab_size":16,
724 "max_position_embeddings":128,"sliding_window":64,
725 "layer_types":["sliding_attention","full_attention"],
726 "rope_parameters":{"full_attention":{"rope_theta":10000},
727 "sliding_attention":{"rope_theta":10000}}}"#,
728 );
729 let capability = CARRIED_PRIME.trunk_capabilities(&gemma).batch;
730 assert!(!capability.supported);
731 assert!(
732 capability
733 .blockers
734 .contains(&OperationKind::SlidingWindowAttention)
735 );
736 assert!(capability.blockers.contains(&OperationKind::GemmaResidual));
737 assert_eq!(decode_batch_program(&gemma), DecodeBatchProgram::Gemma);
738 assert!(!DECODE_GRAPH.trunk_capabilities(&gemma).cuda_graph.supported);
739
740 let mut sliding_gated_moe = plan(
741 r#"{"model_type":"qwen3_moe","num_hidden_layers":2,"hidden_size":64,
742 "num_attention_heads":2,"num_key_value_heads":1,"head_dim":32,
743 "intermediate_size":128,"vocab_size":16,"max_position_embeddings":128,
744 "num_experts":4,"num_experts_per_tok":2,"moe_intermediate_size":32}"#,
745 );
746 let crate::model_plan::AttentionPlan::Full(mut attention) =
747 sliding_gated_moe.layers[0].attention.clone()
748 else {
749 unreachable!()
750 };
751 attention.output_gate = crate::config::AttentionGateKind::SeparateHead;
752 sliding_gated_moe.layers[0].attention = crate::model_plan::AttentionPlan::SlidingWindow {
753 attention,
754 window: 64,
755 };
756 let crate::model_plan::MlpPlan::Moe(moe) = &mut sliding_gated_moe.layers[0].mlp else {
757 unreachable!()
758 };
759 moe.router = crate::model_plan::RouterPlan::Sigmoid {
760 normalize_selected: true,
761 scaling_factor: 1.0,
762 selection_bias: false,
763 };
764 assert_eq!(
765 decode_batch_program(&sliding_gated_moe),
766 DecodeBatchProgram::SlidingGatedMoe
767 );
768 assert!(
769 !DECODE_GRAPH
770 .trunk_capabilities(&sliding_gated_moe)
771 .cuda_graph
772 .supported
773 );
774 assert_eq!(decode_batch_program(&dense), DecodeBatchProgram::Generic);
775 assert_eq!(
776 MTP_SPEC.capabilities(&dense).speculative.blockers,
777 vec![OperationKind::DraftPlan]
778 );
779
780 let qwen35 = crate::model_packs::by_alias("qwen35")
781 .unwrap()
782 .compile_tiny_plan()
783 .unwrap();
784 assert!(MTP_SPEC.capabilities(&qwen35).speculative.supported);
785
786 let dsv4 = crate::model_packs::by_alias("deepseek_v4_dspark")
787 .unwrap()
788 .compile_tiny_plan()
789 .unwrap();
790 let dsv4_batch = DECODE_BATCH.trunk_capabilities(&dsv4).batch;
791 assert!(!dsv4_batch.supported);
792 assert!(
793 dsv4_batch
794 .blockers
795 .contains(&OperationKind::CompressedMlaAttention)
796 );
797 assert!(!MTP_SPEC.capabilities(&dsv4).speculative.supported);
798
799 let root = std::env::temp_dir().join(format!(
800 "memra-plan-backend-external-draft-{}",
801 std::process::id()
802 ));
803 std::fs::create_dir_all(&root).unwrap();
804 let trunk_path = root.join("trunk.gguf");
805 let draft_path = root.join("draft.gguf");
806 crate::micro_gguf::write_step35_meta_only(&trunk_path).unwrap();
807 crate::micro_gguf::write_step35_mtp_meta_only(&draft_path).unwrap();
808 let trunk_cfg = ModelConfig::from_gguf(&crate::GgufFile::open(&trunk_path).unwrap());
809 let draft_cfg = ModelConfig::from_gguf(&crate::GgufFile::open(&draft_path).unwrap());
810 let mut external = crate::model_packs::for_config(&trunk_cfg)
811 .unwrap()
812 .compile_plan(&trunk_cfg)
813 .unwrap();
814 let draft = crate::model_packs::for_config(&draft_cfg)
815 .unwrap()
816 .compile_plan(&draft_cfg)
817 .unwrap();
818 assert_eq!(
819 external.draft_source,
820 crate::model_plan::DraftSourcePlan::ExternalArtifact
821 );
822 external.attach_external_draft(&draft).unwrap();
823 assert!(MTP_SPEC.capabilities(&external).speculative.supported);
824 std::fs::remove_dir_all(root).unwrap();
825 }
826
827 #[test]
828 fn rewrite_receipts_bind_plan_program_binary_and_outputs() {
829 let dense = plan(
830 r#"{"model_type":"qwen3","num_hidden_layers":2,"hidden_size":64,
831 "num_attention_heads":2,"num_key_value_heads":1,"head_dim":32,
832 "intermediate_size":128,"vocab_size":16,"max_position_embeddings":128}"#,
833 );
834 let rewrites = execution_rewrites(&dense);
835 let batch = rewrites
836 .iter()
837 .find(|rewrite| rewrite.surface == RewriteSurface::DecodeBatch)
838 .unwrap();
839 assert!(batch.eligible());
840 let spec = rewrites
841 .iter()
842 .find(|rewrite| rewrite.surface == RewriteSurface::MtpSpec)
843 .unwrap();
844 assert_eq!(spec.blockers, vec![OperationKind::DraftPlan]);
845
846 let policy = RewriteParityPolicy {
847 max_abs: 0.01,
848 max_rel: 0.01,
849 require_argmax: true,
850 };
851 let implementation = "00".repeat(32);
852 let artifact_lock = b"format_version=2\nfamily=qwen3\n";
853 let receipt = batch
854 .verify_logits(
855 &implementation,
856 &[0.0, 1.0, -1.0],
857 &[0.0, 1.001, -1.001],
858 policy,
859 )
860 .unwrap()
861 .bind_artifact_lock(artifact_lock);
862 assert!(receipt.passed);
863 receipt.validate_for(batch).unwrap();
864 let tsv = receipt.to_tsv();
865 assert!(tsv.contains("rewrite\tdecode-batch.v1"));
866 assert!(tsv.contains(&format!("plan_sha256\t{}", batch.plan_sha256)));
867 assert!(tsv.contains(&format!("implementation_sha256\t{implementation}")));
868
869 let root = std::env::temp_dir().join(format!(
870 "memra-rewrite-qualification-{}",
871 std::process::id()
872 ));
873 let receipts = root.join("rewrite-receipts");
874 std::fs::create_dir_all(&receipts).unwrap();
875 std::fs::write(root.join("artifact.lock"), artifact_lock).unwrap();
876 std::fs::write(receipts.join("decode-batch.v1.tsv"), &tsv).unwrap();
877 std::fs::write(
878 root.join("rewrite-receipts.tsv"),
879 format!(
880 "rewrite\tplan_sha256\treceipt_sha256\tstatus\n\
881 decode-batch.v1\t{}\t{}\tpassed\n",
882 batch.plan_sha256,
883 hex_sha256(tsv.as_bytes())
884 ),
885 )
886 .unwrap();
887 let qualifications = RewriteQualifications::load(&root, &dense).unwrap();
888 assert!(qualifications.allows(RewriteSurface::DecodeBatch));
889 assert!(!qualifications.allows(RewriteSurface::DecodeGraph));
890 std::fs::write(receipts.join("decode-batch.v1.tsv"), "tampered").unwrap();
891 assert!(RewriteQualifications::load(&root, &dense).is_err());
892 std::fs::remove_dir_all(root).unwrap();
893
894 let failed = batch
895 .verify_logits(
896 &implementation,
897 &[0.0, 1.0, -1.0],
898 &[2.0, 1.0, -1.0],
899 policy,
900 )
901 .unwrap();
902 assert!(!failed.passed);
903 assert!(failed.validate_for(batch).is_err());
904
905 let other = plan(
906 r#"{"model_type":"qwen3","num_hidden_layers":3,"hidden_size":64,
907 "num_attention_heads":2,"num_key_value_heads":1,"head_dim":32,
908 "intermediate_size":128,"vocab_size":16,"max_position_embeddings":128}"#,
909 );
910 let other_batch = execution_rewrites(&other)
911 .into_iter()
912 .find(|rewrite| rewrite.surface == RewriteSurface::DecodeBatch)
913 .unwrap();
914 assert!(receipt.validate_for(&other_batch).is_err());
915
916 let qwen35 = crate::model_packs::by_alias("qwen35")
917 .unwrap()
918 .compile_tiny_plan()
919 .unwrap();
920 let qwen35_spec = execution_rewrites(&qwen35)
921 .into_iter()
922 .find(|rewrite| rewrite.surface == RewriteSurface::MtpSpec)
923 .unwrap();
924 assert!(qwen35_spec.eligible());
925 }
926}