diff --git a/src/rsprocess/bisimilarity.rs b/src/rsprocess/bisimilarity.rs new file mode 100644 index 0000000..7443e08 --- /dev/null +++ b/src/rsprocess/bisimilarity.rs @@ -0,0 +1,174 @@ +use std::collections::{BTreeSet, HashMap}; + +use petgraph::visit::{EdgeIndexable, EdgeRef, IntoEdgeReferences, IntoEdges, IntoNodeReferences, NodeIndexable}; + + + + + + +#[allow(dead_code)] +fn print_type_of(_: &T) { + println!("type: {}", std::any::type_name::()); +} + + + + + + +struct GraphPartition<'a, G> +where + G: EdgeIndexable + NodeIndexable + IntoEdgeReferences + IntoNodeReferences + + IntoEdges, + G::NodeId: std::cmp::Eq + std::hash::Hash, +{ + pub node_to_block: HashMap<(usize, G::NodeId), u32>, + pub block_to_node: HashMap>, + pub graphs: [&'a G; 2], + last_block: u32, + blocks: BTreeSet +} + +impl<'a, G> GraphPartition<'a, G> +where + G: EdgeIndexable + NodeIndexable + IntoEdgeReferences + IntoNodeReferences + + IntoEdges, + G::NodeId: std::cmp::Eq + std::hash::Hash, +{ + pub fn new(graph_a: &'a G, graph_b: &'a G) -> Self { + GraphPartition { node_to_block: HashMap::new(), + block_to_node: HashMap::new(), + graphs: [graph_a, graph_b], + last_block: 0, + blocks: BTreeSet::new() } + } + + #[inline(always)] + pub fn add_node_last_partition(&mut self, node: G::NodeId, graph: usize) { + self.node_to_block.insert((graph, node), self.last_block); + self.block_to_node + .entry(self.last_block) + .or_default() + .push((graph, node)); + self.blocks.insert(self.last_block); + } + + pub fn iterate_blocks(&self) -> Vec { + self.blocks.iter().cloned().collect::>() + } + + pub fn bisimilar(&self) -> bool { + // if there is a block that has only elements from one graph then they are + // not bisimilar + for (_block, node_list) in self.block_to_node.iter() { + let graph_id = node_list.first().unwrap().0; + if node_list.iter().all(|el| el.0 == graph_id) { + return false; + } + } + true + } + + fn reachable_blocks( + &self, + label: &G::EdgeRef, + s: &(usize, G::NodeId) + ) -> Vec<(usize, G::NodeId)> + where ::EdgeRef: PartialEq + { + let mut val = vec![]; + for el in self.graphs[s.0].edges(s.1).filter(|x| x == label) { + val.push((s.0, el.target())); + } + val + } + + pub fn split(&mut self, block: u32, label: &G::EdgeRef) -> bool + where ::EdgeRef: PartialEq + { + let Some(nodes) = self.block_to_node.get(&block) + else { + return true + }; + let mut nodes = nodes.iter(); + let s = nodes.next().unwrap(); + + let mut b1 = vec![s]; + let mut b2 = vec![]; + + let reachable_blocks_s = self.reachable_blocks(label, s); + + 'outer: for t in nodes { + let reachable_blocks_t = self.reachable_blocks(label, t); + for rbt in reachable_blocks_t { + if !reachable_blocks_s.contains(&rbt) { + b2.push(*t); + continue 'outer; + } + } + b1.push(t); + } + + if b2.is_empty() { + // all elements go to the same block with label label, so no change + false + } else { + // some elements need to be split into a different block + self.last_block += 1; + let new_block = self.last_block; + self.blocks.insert(new_block); + + for b in b2 { + self.node_to_block.entry(b).and_modify(|e| *e = new_block); + self.block_to_node.entry(new_block).or_default().push(b); + self.block_to_node.entry(block).and_modify(|e| { + let index = e.iter().position(|x| *x == b).unwrap(); + e.remove(index); + }); + } + true + } + } +} + +pub fn bisimilarity_kanellakis_smolka<'a, G>( + graph_a: &'a G, + graph_b: &'a G +) -> bool +where + G: EdgeIndexable + NodeIndexable + IntoEdgeReferences + IntoNodeReferences + + IntoEdges, + G::NodeId: std::cmp::Eq + std::hash::Hash, + G::EdgeRef: PartialEq +{ + let graphs = [graph_a, graph_b]; + + let mut partition: GraphPartition = GraphPartition::new(graph_a, graph_b); + for (p, graph) in graphs.iter().enumerate() { + for node in graph.node_identifiers() { + partition.add_node_last_partition(node, p); + } + } + + let labels = + graph_a.edge_references() + .chain(graph_b.edge_references()) + .collect::>(); + + let mut changed = true; + + while changed { + changed = false; + + for block in partition.iterate_blocks() { + for label in labels.iter() { + if partition.split(block, label) { + changed = true; + } + } + } + } + + partition.bisimilar() +}