use std::collections::{BTreeSet, HashMap, VecDeque};
use crate::file::Rete;
use crate::terms::NodeId;
pub fn build_adjacency(rete: &Rete, pred: &str) -> HashMap<NodeId, Vec<NodeId>> {
let mut adj: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
for (s, o) in rete.predicate_pairs(pred) {
adj.entry(s).or_default().push(o);
}
adj
}
pub fn reach_one(adj: &HashMap<NodeId, Vec<NodeId>>, seed: NodeId) -> BTreeSet<NodeId> {
let mut visited = BTreeSet::new();
let mut queue = VecDeque::new();
if let Some(succ) = adj.get(&seed) {
for &n in succ {
if visited.insert(n) {
queue.push_back(n);
}
}
}
while let Some(n) = queue.pop_front() {
if let Some(succ) = adj.get(&n) {
for &m in succ {
if visited.insert(m) {
queue.push_back(m);
}
}
}
}
visited
}
pub fn batch_reach_serial(
adj: &HashMap<NodeId, Vec<NodeId>>,
seeds: &[NodeId],
) -> Vec<BTreeSet<NodeId>> {
seeds.iter().map(|&s| reach_one(adj, s)).collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dictionary::DictionaryBuilder;
use crate::file::{build_pyramid_meta, write_dataset, Rete, DEFAULT_TILE_BUDGET};
use crate::index::GraphIndexBuilder;
fn fixture() -> Rete {
let edges = [
("A", "B"),
("B", "C"),
("A", "C"),
("C", "A"),
("D", "E"),
("E", "F"),
("D", "F"),
("F", "D"),
("C", "D"),
];
let mut db = DictionaryBuilder::new();
for (s, o) in edges {
db.observe(s, "knows", o);
}
let dict = db.build();
let mut triples: Vec<(u32, u32, u32)> = edges
.iter()
.map(|(s, o)| dict.encode(s, "knows", o).unwrap())
.collect();
triples.sort_unstable();
triples.dedup();
let mut def = GraphIndexBuilder::new();
for &t in &triples {
def.push(t);
}
let (meta, levels) = build_pyramid_meta(&dict, &triples, DEFAULT_TILE_BUDGET);
let bytes = write_dataset(&dict, &def.build(), &[], false, &meta, levels);
Rete::open(&bytes).unwrap()
}
#[test]
fn build_adjacency_and_reach() {
let rete = fixture();
let adj = build_adjacency(&rete, "knows");
let dict = rete.dictionary();
let a = dict.node_of_term("A").unwrap();
let reached = reach_one(&adj, a);
assert_eq!(reached.len(), 6);
}
#[test]
fn batch_serial_matches_single() {
let rete = fixture();
let adj = build_adjacency(&rete, "knows");
let seeds: Vec<u32> = adj.keys().copied().collect();
let batch = batch_reach_serial(&adj, &seeds);
for (i, &s) in seeds.iter().enumerate() {
assert_eq!(batch[i], reach_one(&adj, s));
}
assert!(batch.iter().any(|r| r.len() >= 4));
}
}