#[cfg(any(test, feature = "cpu-parity"))]
use super::validate::validate_persistent_bfs_inputs;
#[must_use]
#[cfg(any(test, feature = "cpu-parity"))]
pub fn cpu_ref(
node_count: u32,
edge_offsets: &[u32],
edge_targets: &[u32],
edge_kind_mask: &[u32],
frontier_in: &[u32],
allow_mask: u32,
max_iters: u32,
) -> (Vec<u32>, u32) {
try_cpu_ref(
node_count,
edge_offsets,
edge_targets,
edge_kind_mask,
frontier_in,
allow_mask,
max_iters,
)
.expect(
"Fix: reject malformed CSR/frontier via try_cpu_ref; parity wrappers must not pass hostile layouts",
)
}
#[cfg(any(test, feature = "cpu-parity"))]
pub fn try_cpu_ref(
node_count: u32,
edge_offsets: &[u32],
edge_targets: &[u32],
edge_kind_mask: &[u32],
frontier_in: &[u32],
allow_mask: u32,
max_iters: u32,
) -> Result<(Vec<u32>, u32), String> {
let mut out = Vec::new();
let changed = try_cpu_ref_into(
node_count,
edge_offsets,
edge_targets,
edge_kind_mask,
frontier_in,
allow_mask,
max_iters,
&mut out,
)?;
Ok((out, changed))
}
#[cfg(any(test, feature = "cpu-parity"))]
#[derive(Debug, Default, Clone)]
pub(crate) struct PersistentBfsCpuScratch {
pub step: Vec<u32>,
}
#[cfg(any(test, feature = "cpu-parity"))]
impl PersistentBfsCpuScratch {
pub(crate) fn new() -> Self {
Self::default()
}
}
#[cfg(any(test, feature = "cpu-parity"))]
pub(crate) fn cpu_ref_into(
node_count: u32,
edge_offsets: &[u32],
edge_targets: &[u32],
edge_kind_mask: &[u32],
frontier_in: &[u32],
allow_mask: u32,
max_iters: u32,
frontier_out: &mut Vec<u32>,
) -> u32 {
let mut scratch = PersistentBfsCpuScratch::default();
try_cpu_ref_into_with_scratch(
node_count,
edge_offsets,
edge_targets,
edge_kind_mask,
frontier_in,
allow_mask,
max_iters,
frontier_out,
&mut scratch,
)
.expect(
"Fix: reject malformed CSR/frontier via try_cpu_ref_into; parity wrappers must not pass hostile layouts",
)
}
#[cfg(any(test, feature = "cpu-parity"))]
pub fn try_cpu_ref_into(
node_count: u32,
edge_offsets: &[u32],
edge_targets: &[u32],
edge_kind_mask: &[u32],
frontier_in: &[u32],
allow_mask: u32,
max_iters: u32,
frontier_out: &mut Vec<u32>,
) -> Result<u32, String> {
let mut scratch = PersistentBfsCpuScratch::default();
try_cpu_ref_into_with_scratch(
node_count,
edge_offsets,
edge_targets,
edge_kind_mask,
frontier_in,
allow_mask,
max_iters,
frontier_out,
&mut scratch,
)
}
#[cfg(any(test, feature = "cpu-parity"))]
pub(crate) fn try_cpu_ref_into_with_scratch(
node_count: u32,
edge_offsets: &[u32],
edge_targets: &[u32],
edge_kind_mask: &[u32],
frontier_in: &[u32],
allow_mask: u32,
max_iters: u32,
frontier_out: &mut Vec<u32>,
scratch: &mut PersistentBfsCpuScratch,
) -> Result<u32, String> {
let layout = validate_persistent_bfs_inputs(
node_count,
edge_offsets,
edge_targets,
edge_kind_mask,
frontier_in,
)?;
let words = layout.words;
crate::graph::scratch::reserve_graph_items(
frontier_out,
words,
"persistent BFS CPU oracle",
"frontier output",
)?;
crate::graph::scratch::reserve_graph_items(
&mut scratch.step,
words,
"persistent BFS CPU oracle",
"per-iteration frontier scratch",
)?;
frontier_out.clear();
frontier_out.extend_from_slice(frontier_in);
frontier_out.resize(words, 0);
scratch.step.clear();
scratch.step.resize(words, 0);
let mut changed = 0u32;
for _ in 0..max_iters {
crate::graph::csr_forward_traverse::cpu_ref_into(
node_count,
edge_offsets,
edge_targets,
edge_kind_mask,
frontier_out,
allow_mask,
&mut scratch.step,
);
let mut step_changed = false;
for w in 0..words {
let old = frontier_out[w];
frontier_out[w] |= scratch.step[w];
if frontier_out[w] != old {
step_changed = true;
}
}
if step_changed {
changed = 1;
} else {
break;
}
}
Ok(changed)
}