metrale_model_layers/circuit_exec/
routes.rs1use anyhow::{Result, bail, ensure};
17use metrale_circuit::{FusionPlan, RuntimeRoute};
18
19use super::program::{GdnState, Program};
20
21pub const KNOWN: [&str; 1] = ["gdn_state_slots_fragmented"];
23
24pub struct RoutedProgram {
26 pub route: RuntimeRoute,
28 pub program: Program,
30 pub plan: FusionPlan,
32}
33
34pub fn check_known(runtime: &[RuntimeRoute]) -> Result<()> {
36 if let Some(r) = runtime.iter().find(|r| !KNOWN.contains(&r.id.as_str())) {
37 bail!(
38 "runtime route `{}` has no evaluator in the executor (it knows {KNOWN:?})",
39 r.id
40 );
41 }
42 Ok(())
43}
44
45pub fn holds(
48 route: &RuntimeRoute,
49 pitch: &[Option<(usize, usize)>],
50 gdn: &[Vec<GdnState>],
51) -> Result<bool> {
52 match route.id.as_str() {
53 "gdn_state_slots_fragmented" => fragmented(pitch, gdn),
54 other => bail!("runtime route `{other}` has no evaluator"),
55 }
56}
57
58fn fragmented(pitch: &[Option<(usize, usize)>], gdn: &[Vec<GdnState>]) -> Result<bool> {
59 let mut verdict: Option<(usize, bool)> = None;
60 for (layer, (p, rows)) in pitch.iter().zip(gdn).enumerate() {
61 let (Some((h, conv)), Some(base)) = (p, rows.first()) else {
62 continue;
63 };
64 let contiguous = rows
65 .iter()
66 .enumerate()
67 .all(|(i, s)| s.h == base.h.offset(i * h) && s.conv == base.conv.offset(i * conv));
68 match verdict {
69 None => verdict = Some((layer, contiguous)),
70 Some((first, c)) => ensure!(
71 c == contiguous,
72 "GDN layers {first} and {layer} disagree on slot contiguity"
73 ),
74 }
75 }
76 Ok(verdict.is_some_and(|(_, contiguous)| !contiguous))
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use metrale_gpu_runtime::gpu::DevicePtr;
83
84 fn state(h: u64, conv: u64) -> GdnState {
85 GdnState {
86 h: DevicePtr(h),
87 conv: DevicePtr(conv),
88 h_steps: [DevicePtr::NULL; super::super::program::MAX_VERIFY_STEPS],
89 conv_steps: [DevicePtr::NULL; super::super::program::MAX_VERIFY_STEPS],
90 }
91 }
92
93 fn route(id: &str) -> RuntimeRoute {
94 RuntimeRoute {
95 id: id.into(),
96 when: Default::default(),
97 modes: Default::default(),
98 rows: (2, 128),
99 plans_as: Default::default(),
100 why: "test".into(),
101 cite: "test".into(),
102 }
103 }
104
105 #[test]
108 fn fragmented_holds_exactly_when_a_row_is_out_of_place() {
109 let r = route("gdn_state_slots_fragmented");
110 let pitch = [Some((0x100, 0x10)), None, Some((0x100, 0x10))];
111 let layer = |rows: &[(u64, u64)]| -> Vec<GdnState> {
112 rows.iter().map(|&(h, c)| state(h, c)).collect()
113 };
114 let ok: Vec<GdnState> = layer(&[(0x1000, 0x50), (0x1100, 0x60), (0x1200, 0x70)]);
115 let gdn = vec![ok.clone(), Vec::new(), ok.clone()];
116 assert!(!holds(&r, &pitch, &gdn).unwrap());
117 for bad in [
118 layer(&[(0x1000, 0x50), (0x1100, 0x60), (0x1300, 0x70)]),
119 layer(&[(0x1000, 0x50), (0x1100, 0x60), (0x1200, 0x80)]),
120 layer(&[(0x1000, 0x50), (0x1200, 0x60), (0x1300, 0x70)]),
121 ] {
122 let gdn = vec![bad.clone(), Vec::new(), bad];
123 assert!(holds(&r, &pitch, &gdn).unwrap());
124 let mixed = vec![ok.clone(), Vec::new(), gdn[0].clone()];
125 assert!(
126 holds(&r, &pitch, &mixed)
127 .unwrap_err()
128 .to_string()
129 .contains("disagree")
130 );
131 }
132 }
133
134 #[test]
135 fn an_unknown_route_is_refused_at_build() {
136 assert!(check_known(&[route("gdn_state_slots_fragmented")]).is_ok());
137 let e = check_known(&[route("mystery")]).unwrap_err().to_string();
138 assert!(e.contains("mystery") && e.contains("no evaluator"), "{e}");
139 }
140}