scc impl
parent
d2070199f7
commit
3fb032c9a7
@ -0,0 +1,176 @@
|
|||||||
|
use std::cmp::min;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use crate::algo::AlgoImpl;
|
||||||
|
use crate::data::expr::Expr;
|
||||||
|
use crate::data::program::{MagicAlgoRuleArg, MagicSymbol};
|
||||||
|
use crate::data::symb::Symbol;
|
||||||
|
use crate::data::tuple::Tuple;
|
||||||
|
use crate::data::value::DataValue;
|
||||||
|
use crate::runtime::derived::DerivedRelStore;
|
||||||
|
use crate::runtime::transact::SessionTx;
|
||||||
|
|
||||||
|
pub(crate) struct StronglyConnectedComponent;
|
||||||
|
|
||||||
|
impl AlgoImpl for StronglyConnectedComponent {
|
||||||
|
fn run(
|
||||||
|
&mut self,
|
||||||
|
tx: &mut SessionTx,
|
||||||
|
rels: &[MagicAlgoRuleArg],
|
||||||
|
opts: &BTreeMap<Symbol, Expr>,
|
||||||
|
stores: &BTreeMap<MagicSymbol, DerivedRelStore>,
|
||||||
|
out: &DerivedRelStore,
|
||||||
|
) -> Result<()> {
|
||||||
|
let edges = rels
|
||||||
|
.get(0)
|
||||||
|
.ok_or_else(|| anyhow!("'strongly_connected_components' missing edges relation"))?;
|
||||||
|
|
||||||
|
let reverse_mode = match opts.get(&Symbol::from("mode")) {
|
||||||
|
None => false,
|
||||||
|
Some(Expr::Const(DataValue::String(s))) => match s as &str {
|
||||||
|
"group_first" => true,
|
||||||
|
"key_first" => false,
|
||||||
|
v => bail!(
|
||||||
|
"unexpected option 'mode' for 'strongly_connected_components': {}",
|
||||||
|
v
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Some(v) => bail!(
|
||||||
|
"unexpected option 'mode' for 'strongly_connected_components': {:?}",
|
||||||
|
v
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut graph: Vec<Vec<usize>> = vec![];
|
||||||
|
let mut indices: Vec<DataValue> = vec![];
|
||||||
|
let mut inv_indices: BTreeMap<DataValue, usize> = Default::default();
|
||||||
|
|
||||||
|
for tuple in edges.iter(tx, stores)? {
|
||||||
|
let mut tuple = tuple?.0.into_iter();
|
||||||
|
let from = tuple.next().ok_or_else(|| {
|
||||||
|
anyhow!("edges relation for 'strongly_connected_components' too short")
|
||||||
|
})?;
|
||||||
|
let to = tuple.next().ok_or_else(|| {
|
||||||
|
anyhow!("edges relation for 'strongly_connected_components' too short")
|
||||||
|
})?;
|
||||||
|
let from_idx = if let Some(idx) = inv_indices.get(&from) {
|
||||||
|
*idx
|
||||||
|
} else {
|
||||||
|
inv_indices.insert(from.clone(), graph.len());
|
||||||
|
indices.push(from.clone());
|
||||||
|
graph.push(vec![]);
|
||||||
|
graph.len() - 1
|
||||||
|
};
|
||||||
|
let to_idx = if let Some(idx) = inv_indices.get(&to) {
|
||||||
|
*idx
|
||||||
|
} else {
|
||||||
|
inv_indices.insert(to.clone(), graph.len());
|
||||||
|
indices.push(to.clone());
|
||||||
|
graph.push(vec![]);
|
||||||
|
graph.len() - 1
|
||||||
|
};
|
||||||
|
let from_target = graph.get_mut(from_idx).unwrap();
|
||||||
|
from_target.push(to_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tarjan = TarjanScc::new(&graph).run();
|
||||||
|
for (grp_id, cc) in tarjan.iter().enumerate() {
|
||||||
|
for idx in cc {
|
||||||
|
let val = indices.get(*idx).unwrap();
|
||||||
|
let tuple = if reverse_mode {
|
||||||
|
Tuple(vec![DataValue::from(grp_id as i64), val.clone()])
|
||||||
|
} else {
|
||||||
|
Tuple(vec![val.clone(), DataValue::from(grp_id as i64)])
|
||||||
|
};
|
||||||
|
out.put(tuple, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut counter = tarjan.len() as i64;
|
||||||
|
|
||||||
|
if let Some(nodes) = rels.get(1) {
|
||||||
|
for tuple in nodes.iter(tx, stores)? {
|
||||||
|
let tuple = tuple?;
|
||||||
|
let node = tuple.0.into_iter().next().ok_or_else(|| {
|
||||||
|
anyhow!("nodes relation for 'strongly_connected_components' too short")
|
||||||
|
})?;
|
||||||
|
if !inv_indices.contains_key(&node) {
|
||||||
|
inv_indices.insert(node.clone(), usize::MAX);
|
||||||
|
let tuple = if reverse_mode {
|
||||||
|
Tuple(vec![DataValue::from(counter), node])
|
||||||
|
} else {
|
||||||
|
Tuple(vec![node, DataValue::from(counter)])
|
||||||
|
};
|
||||||
|
out.put(tuple, 0);
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct TarjanScc<'a> {
|
||||||
|
graph: &'a [Vec<usize>],
|
||||||
|
id: usize,
|
||||||
|
ids: Vec<Option<usize>>,
|
||||||
|
low: Vec<usize>,
|
||||||
|
on_stack: Vec<bool>,
|
||||||
|
stack: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TarjanScc<'a> {
|
||||||
|
pub(crate) fn new(graph: &'a [Vec<usize>]) -> 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<Vec<usize>> {
|
||||||
|
for i in 0..self.graph.len() {
|
||||||
|
if self.ids[i].is_none() {
|
||||||
|
self.dfs(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut low_map: BTreeMap<usize, Vec<usize>> = BTreeMap::new();
|
||||||
|
for (idx, grp) in self.low.into_iter().enumerate() {
|
||||||
|
low_map.entry(grp).or_default().push(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
low_map.into_iter().map(|(_, vs)| vs).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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue