1use vyre_primitives::graph::exploded::{build_cpu_reference, dense_to_encoded, encoded_to_dense};
11
12#[must_use]
17pub fn build_ifds_csr(
18 num_procs: u32,
19 blocks_per_proc: u32,
20 facts_per_proc: u32,
21 intra_edges: &[(u32, u32, u32)],
22 inter_edges: &[(u32, u32, u32, u32)],
23 flow_gen: &[(u32, u32, u32)],
24 flow_kill: &[(u32, u32, u32)],
25) -> (Vec<u32>, Vec<u32>) {
26 use crate::observability::{bump, dataflow_fixpoint_calls};
27 bump(&dataflow_fixpoint_calls);
28 build_cpu_reference(
29 num_procs,
30 blocks_per_proc,
31 facts_per_proc,
32 intra_edges,
33 inter_edges,
34 flow_gen,
35 flow_kill,
36 )
37}
38
39#[must_use]
44pub fn ifds_node_count(num_procs: u32, blocks_per_proc: u32, facts_per_proc: u32) -> u32 {
45 num_procs
46 .saturating_mul(blocks_per_proc)
47 .saturating_mul(facts_per_proc)
48}
49
50#[must_use]
54pub fn round_trip_dense(dense: u32, blocks_per_proc: u32, facts_per_proc: u32) -> Option<u32> {
55 let encoded = dense_to_encoded(dense, blocks_per_proc, facts_per_proc)?;
56 encoded_to_dense(encoded, blocks_per_proc, facts_per_proc)
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
67 fn csr_row_count_matches_node_count() {
68 let (row_ptr, _) = build_ifds_csr(2, 2, 2, &[(0, 0, 1), (1, 0, 1)], &[], &[], &[]);
69 assert_eq!(row_ptr.len(), 9);
71 assert_eq!(ifds_node_count(2, 2, 2), 8);
72 }
73
74 #[test]
76 fn matches_primitive_directly() {
77 let intra = vec![(0, 0, 1), (1, 0, 1)];
78 let inter = vec![(0, 1, 1, 0)];
79 let gen = vec![(0, 0, 1)];
80 let kill = vec![(1, 0, 0)];
81 let via_substrate = build_ifds_csr(2, 2, 2, &intra, &inter, &gen, &kill);
82 let via_primitive = build_cpu_reference(2, 2, 2, &intra, &inter, &gen, &kill);
83 assert_eq!(via_substrate, via_primitive);
84 }
85
86 #[test]
90 fn empty_graph_yields_singleton_row_ptr() {
91 let (row_ptr, col_idx) = build_ifds_csr(0, 0, 0, &[], &[], &[], &[]);
92 assert_eq!(row_ptr, vec![0u32]);
93 assert!(col_idx.is_empty());
94 }
95
96 #[test]
100 fn kill_suppresses_fact_propagation() {
101 let intra = vec![(0, 0, 1)];
102 let kill = vec![(0, 0, 1)];
103 let (row_ptr, col_idx) = build_ifds_csr(1, 2, 2, &intra, &[], &[], &kill);
104 let src = 1usize;
106 let row_start = row_ptr[src] as usize;
107 let row_end = row_ptr[src + 1] as usize;
108 let neighbors = &col_idx[row_start..row_end];
109 assert!(!neighbors.contains(&3), "killed fact must not propagate");
111 }
112
113 #[test]
117 fn gen_injects_new_fact() {
118 let intra = vec![(0, 0, 1)];
119 let gen = vec![(0, 0, 1)];
120 let (row_ptr, col_idx) = build_ifds_csr(1, 2, 2, &intra, &[], &gen, &[]);
121 let row_start = row_ptr[0] as usize;
123 let row_end = row_ptr[1] as usize;
124 let neighbors = &col_idx[row_start..row_end];
125 assert!(neighbors.contains(&3), "gen must emit edge to new fact");
127 }
128
129 #[test]
131 fn round_trip_dense_is_identity() {
132 let blocks_per_proc = 4;
133 let facts_per_proc = 8;
134 for dense in 0..32 {
135 assert_eq!(
136 round_trip_dense(dense, blocks_per_proc, facts_per_proc),
137 Some(dense)
138 );
139 }
140 }
141
142 #[test]
146 fn inter_edge_propagates_every_fact() {
147 let inter = vec![(0, 0, 1, 1)];
148 let (row_ptr, col_idx) = build_ifds_csr(2, 2, 2, &[], &inter, &[], &[]);
149 let dense_src_f0 = 0; let dense_src_f1 = 1; let row0 = &col_idx[row_ptr[dense_src_f0] as usize..row_ptr[dense_src_f0 + 1] as usize];
152 let row1 = &col_idx[row_ptr[dense_src_f1] as usize..row_ptr[dense_src_f1 + 1] as usize];
153 assert!(row0.contains(&6), "fact 0 must propagate via inter edge");
156 assert!(row1.contains(&7), "fact 1 must propagate via inter edge");
157 }
158}