Expand description
SCC Detection Algorithm
Finally, let us examine a very basic algorithm built using this library: symbolic detection of SCC components.
First, we need to define simple forward/backward reachability procedures:
use biodivine_lib_param_bn::symbolic_async_graph::{GraphColoredVertices, SymbolicAsyncGraph};
use biodivine_lib_param_bn::biodivine_std::traits::Set;
fn fwd(graph: &SymbolicAsyncGraph, initial: &GraphColoredVertices) -> GraphColoredVertices {
let mut result = initial.clone();
loop {
let post = graph.post(&result);
if post.is_subset(&result) {
return result;
} else {
result = result.union(&post);
}
}
}
fn bwd(graph: &SymbolicAsyncGraph, initial: &GraphColoredVertices) -> GraphColoredVertices {
let mut result = initial.clone();
loop {
let post = graph.pre(&result);
if post.is_subset(&result) {
return result;
} else {
result = result.union(&post);
}
}
}
Note that in practice, it would be much better to use something like saturation, where
the transitions which are applied are selected greedily one at a time, instead of applying
them all at once using full post
or pre
. However, for the purpose of this tutorial, this
should be sufficient.
Now we can observe that an SCC of vertex v
is always the intersection of forward and backward
reachable vertices from v
. Hence we can write the following simple algorithm:
fn scc(graph: &SymbolicAsyncGraph) -> Vec<GraphColoredVertices> {
let mut universe = graph.mk_unit_colored_vertices();
let mut components = Vec::new();
while !universe.is_empty() {
// Pick one vertex in universe for every color in universe
let pivots = universe.pick_vertex();
let fwd = fwd(graph, &pivots);
let bwd = bwd(graph, &pivots);
let scc = fwd.intersect(&bwd);
universe = universe.minus(&scc);
components.push(scc);
}
return components;
}
// Boolean network from the previous tutorial:
let bn = BooleanNetwork::try_from(r"
A -> B
C -|? B
$B: A
C -> A
B -> A
A -| A
$A: C | f(A, B)
").unwrap();
let stg = SymbolicAsyncGraph::new(bn).unwrap();
// Note that since the symbolic graph contains different transitions for different colors,
// this will create SCCs that overlap for some colors but are completely different for others.
// Hence the same vertex can appear in multiple SCCs for different colors.
let components = scc(&stg);
assert_eq!(7, components.len());
assert_eq!(2.0, components[0].vertices().approx_cardinality());
assert_eq!(1.0, components[1].vertices().approx_cardinality());
assert_eq!(2.0, components[2].vertices().approx_cardinality());
assert_eq!(1.0, components[3].vertices().approx_cardinality());
assert_eq!(2.0, components[4].vertices().approx_cardinality());
assert_eq!(2.0, components[5].vertices().approx_cardinality());
assert_eq!(1.0, components[6].vertices().approx_cardinality());