Skip to content

Commit 5015e28

Browse files
selection: test unique predecessors theorem for collect_commactor_routing_tree
Summary: adds a property-based test validating the “Unique Predecessor Theorem” under `collect_commactor_routing_tree`. this test asserts that in the CommActor-style multicast routing simulation: - each destination is forwarded to by at most one distinct peer (i.e., no fan-in), - and no node forwards to itself (`x → x`), a stricter property than general breadth-first routing. we reconstruct the predecessor relation from `tree.forwards`, checking for violations across 256 randomly generated selections and mesh topologies. this complements the earlier test for `collect_routed_paths` and reinforces structural routing invariants. Reviewed By: moonli Differential Revision: D75016108 fbshipit-source-id: 75db6eb8de91b928a771252d4a8284a5e5cf3e17
1 parent 44ca66c commit 5015e28

File tree

1 file changed

+70
-0
lines changed

1 file changed

+70
-0
lines changed

ndslice/src/strategy.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ pub fn gen_selection(depth: u32, shape: Vec<usize>, dim: usize) -> BoxedStrategy
142142
}
143143

144144
mod tests {
145+
use std::collections::HashMap;
145146
use std::collections::HashSet;
146147

147148
use proptest::strategy::ValueTree;
@@ -417,4 +418,73 @@ mod tests {
417418
}
418419
}
419420
}
421+
422+
// Property test: Unique Predecessor Theorem (CommActor Routing)
423+
//
424+
// This test verifies structural invariants of the routing graph
425+
// produced by `collect_commactor_routing_tree`, which simulates
426+
// CommActor-style peer-to-peer multicast forwarding.
427+
//
428+
// ───────────────────────────────────────────────────────────────
429+
// Unique Predecessor Theorem
430+
//
431+
// In a full routing traversal, each coordinate `x` is the target
432+
// of at most one `RoutingStep::Forward` from a distinct
433+
// coordinate `y ≠ x`.
434+
//
435+
// Any additional frames that reach `x` arise only from:
436+
// - structural duplication from the same parent node (e.g., via
437+
// unions)
438+
//
439+
// Unlike the general `collect_routed_paths`, CommActor routing
440+
// never performs self-forwarding (`x → x`). This test confirms
441+
// that as well.
442+
proptest! {
443+
#![proptest_config(ProptestConfig {
444+
cases: 256, ..ProptestConfig::default()
445+
})]
446+
#[test]
447+
fn commactor_routed_paths_unique_predecessor(
448+
slice in gen_slice(4, 8)
449+
) {
450+
let shape = slice.sizes().to_vec();
451+
452+
let mut runner = TestRunner::default();
453+
let s = gen_selection(4, shape.clone(), 0).new_tree(&mut runner).unwrap().current();
454+
455+
let tree = collect_commactor_routing_tree(&s, &slice);
456+
457+
let mut preds: HashMap<usize, HashSet<usize>> = HashMap::new();
458+
459+
for (from, frames) in &tree.forwards {
460+
for frame in frames {
461+
let to = slice.location(&frame.here).unwrap();
462+
463+
// We assert that a CommActor never forwards to
464+
// itself.
465+
prop_assert_ne!(
466+
*from, to,
467+
"CommActor forwarded to itself: {} → {} (selection: {})",
468+
from, to, s
469+
);
470+
471+
preds.entry(to).or_default().insert(*from);
472+
}
473+
}
474+
475+
for (node, parents) in preds {
476+
let non_self_preds: Vec<_> = parents.into_iter()
477+
.filter(|&p| p != node)
478+
.collect();
479+
480+
prop_assert!(
481+
non_self_preds.len() <= 1,
482+
"Node {} had multiple non-self predecessors: {:?} (selection: {})",
483+
node,
484+
non_self_preds,
485+
s,
486+
);
487+
}
488+
}
489+
}
420490
}

0 commit comments

Comments
 (0)