From 709159e6e3c3d3b8f2d08c43ad84c1e06c888bdd Mon Sep 17 00:00:00 2001 From: Ziyang Hu Date: Thu, 28 Jul 2022 13:28:24 +0800 Subject: [PATCH] graph algorithms --- src/data/attr.rs | 6 +- src/data/tuple.rs | 2 +- src/lib.rs | 2 + src/query/graph.rs | 133 ++++++++++++++++++++++++++++++++++++++++++ src/query/mod.rs | 3 +- src/query/relation.rs | 4 +- src/runtime/db.rs | 2 +- 7 files changed, 142 insertions(+), 10 deletions(-) create mode 100644 src/query/graph.rs diff --git a/src/data/attr.rs b/src/data/attr.rs index 918f051d..aeac5406 100644 --- a/src/data/attr.rs +++ b/src/data/attr.rs @@ -295,11 +295,7 @@ impl Attribute { "history": self.with_history }) } - pub(crate) fn coerce_value( - &self, - value: DataValue, - ctx: &mut TempIdCtx, - ) -> Result { + pub(crate) fn coerce_value(&self, value: DataValue, ctx: &mut TempIdCtx) -> Result { if self.val_type.is_ref_type() { if let DataValue::String(s) = value { return Ok(DataValue::EnId(ctx.str2tempid(&s, false))); diff --git a/src/data/tuple.rs b/src/data/tuple.rs index cbb83bf4..a9b5b51f 100644 --- a/src/data/tuple.rs +++ b/src/data/tuple.rs @@ -273,7 +273,7 @@ mod tests { let val = Tuple(val); let encoded = val.encode_as_key(TempStoreId(123)); println!("{:x?}", encoded); - let encoded_tuple: EncodedTuple = (&encoded as &[u8]).into(); + let encoded_tuple: EncodedTuple<'_> = (&encoded as &[u8]).into(); println!("{:?}", encoded_tuple.prefix()); println!("{:?}", encoded_tuple.arity()); println!("{:?}", encoded_tuple.get(0)); diff --git a/src/lib.rs b/src/lib.rs index 237cb3c4..5e08d78d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![warn(rust_2018_idioms, future_incompatible)] + #[cfg(not(target_env = "msvc"))] use tikv_jemallocator::Jemalloc; diff --git a/src/query/graph.rs b/src/query/graph.rs new file mode 100644 index 00000000..a50b4ef7 --- /dev/null +++ b/src/query/graph.rs @@ -0,0 +1,133 @@ +use std::cmp::min; +use std::collections::{BTreeMap, BTreeSet}; + +use itertools::Itertools; + +struct TarjanScc<'a> { + graph: &'a [Vec], + id: usize, + ids: Vec>, + low: Vec, + on_stack: Vec, + stack: Vec, +} + +impl<'a> TarjanScc<'a> { + pub(crate) fn new(graph: &'a [Vec]) -> Self { + Self { + graph, + id: 0, + ids: vec![None; graph.len()], + low: vec![0; graph.len()], + on_stack: vec![false; graph.len()], + stack: vec![], + } + } + pub(crate) fn run(mut self) -> Vec> { + for i in 0..self.graph.len() { + if self.ids[i].is_none() { + self.dfs(i) + } + } + self.low + .into_iter() + .enumerate() + .group_by(|(_id, scc)| *scc) + .into_iter() + .map(|(_scc, args)| args.map(|(id, _scc)| id).collect_vec()) + .collect_vec() + } + fn dfs(&mut self, at: usize) { + self.stack.push(at); + self.on_stack[at] = true; + self.id += 1; + self.ids[at] = Some(self.id); + self.low[at] = self.id; + for to in &self.graph[at] { + let to = *to; + if self.ids[to].is_none() { + self.dfs(to); + } + if self.on_stack[to] { + self.low[at] = min(self.low[at], self.low[to]); + } + } + if self.ids[at].unwrap() == self.low[at] { + while let Some(node) = self.stack.pop() { + self.on_stack[node] = false; + self.low[node] = self.ids[at].unwrap(); + if node == at { + break; + } + } + } + } +} + +type Graph = BTreeMap>; + +pub(crate) fn strongly_connected_components(graph: &Graph) -> Vec> + where + T: Ord, +{ + let indices = graph.keys().collect_vec(); + let invert_indices: BTreeMap<_, _> = indices + .iter() + .enumerate() + .map(|(idx, k)| (*k, idx)) + .collect(); + let idx_graph = graph + .values() + .map(|vs| vs.iter().map(|v| invert_indices[v]).collect_vec()) + .collect_vec(); + TarjanScc::new(&idx_graph) + .run() + .into_iter() + .map(|vs| vs.into_iter().map(|i| indices[i]).collect_vec()) + .collect_vec() +} + +struct Reachable<'a, T> { + graph: &'a Graph +} + +impl<'a, T: Ord> Reachable<'a, T> { + fn walk(&self, starting: &T, collected: &mut BTreeSet<&'a T>) { + for el in self.graph.get(starting).unwrap() { + if collected.insert(el) { + self.walk(el, collected); + } + } + } +} + +pub(crate) fn reachable_components<'a, T: Ord>(graph: &'a Graph, start: &'a T) -> BTreeSet<&'a T> { + let mut collected = BTreeSet::from([start]); + let worker = Reachable {graph}; + worker.walk(start, &mut collected); + collected +} + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + + use crate::query::graph::{reachable_components, strongly_connected_components}; + + #[test] + fn test_scc() { + let graph = BTreeMap::from([ + ("a", vec!["b"]), + ("b", vec!["a", "c"]), + ("c", vec!["a", "d", "e"]), + ("d", vec!["e", "e", "e"]), + ("e", vec![]), + ("f", vec![]) + ]); + let scc = strongly_connected_components(&graph); + dbg!(scc); + let reachable = reachable_components(&graph, &"a"); + dbg!(reachable); + + } +} diff --git a/src/query/mod.rs b/src/query/mod.rs index a5021ae2..abfcbb80 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -18,9 +18,10 @@ pub(crate) mod eval; pub(crate) mod pull; pub(crate) mod relation; pub(crate) mod logical; +pub(crate) mod graph; impl SessionTx { - pub fn run_query(&mut self, payload: &JsonValue) -> Result { + pub fn run_query(&mut self, payload: &JsonValue) -> Result> { let vld = match payload.get("since") { None => Validity::current(), Some(v) => Validity::try_from(v)?, diff --git a/src/query/relation.rs b/src/query/relation.rs index 93b08508..7d296bed 100644 --- a/src/query/relation.rs +++ b/src/query/relation.rs @@ -59,7 +59,7 @@ impl FilteredRelation { tx: &'a SessionTx, epoch: Option, use_delta: &BTreeSet, - ) -> TupleIter { + ) -> TupleIter<'a> { let bindings = self.parent.bindings_after_eliminate(); let eliminate_indices = get_eliminate_indices(&bindings, &self.to_eliminate); Box::new( @@ -981,7 +981,7 @@ pub struct StoredDerivedRelation { } impl StoredDerivedRelation { - fn iter(&self, epoch: Option, use_delta: &BTreeSet) -> TupleIter { + fn iter(&self, epoch: Option, use_delta: &BTreeSet) -> TupleIter<'_> { if epoch == Some(0) { return Box::new(iter::empty()); } diff --git a/src/runtime/db.rs b/src/runtime/db.rs index be627596..39fc6113 100644 --- a/src/runtime/db.rs +++ b/src/runtime/db.rs @@ -46,7 +46,7 @@ impl Debug for Db { } impl Db { - pub fn build(builder: DbBuilder) -> Result { + pub fn build(builder: DbBuilder<'_>) -> Result { let db = builder .use_bloom_filter(true, 10., true) .use_capped_prefix_extractor(true, DB_KEY_PREFIX_LEN)