biodivine_lib_param_bn/tutorial/
p04_graph_algorithm_sample.rs

1//! # SCC Detection Algorithm
2//!
3//! Finally, let us examine a very basic algorithm built using this library:
4//! symbolic detection of SCC components.
5//!
6//! First, we need to define simple forward/backward reachability procedures:
7//!
8//! ```rust
9//! use biodivine_lib_param_bn::symbolic_async_graph::{GraphColoredVertices, SymbolicAsyncGraph};
10//! use biodivine_lib_param_bn::biodivine_std::traits::Set;
11//!
12//! fn fwd(graph: &SymbolicAsyncGraph, initial: &GraphColoredVertices) -> GraphColoredVertices {
13//!     let mut result = initial.clone();
14//!     loop {
15//!         let post = graph.post(&result);
16//!         if post.is_subset(&result) {
17//!             return result;
18//!         } else {
19//!             result = result.union(&post);
20//!         }
21//!     }
22//! }
23//!
24//! fn bwd(graph: &SymbolicAsyncGraph, initial: &GraphColoredVertices) -> GraphColoredVertices {
25//!     let mut result = initial.clone();
26//!     loop {
27//!         let post = graph.pre(&result);
28//!         if post.is_subset(&result) {
29//!             return result;
30//!         } else {
31//!             result = result.union(&post);
32//!         }
33//!     }
34//! }
35//! ```
36//!
37//! Note that in practice, it would be much better to use something like *saturation*, where
38//! the transitions which are applied are selected greedily one at a time, instead of applying
39//! them all at once using full `post` or `pre`. However, for the purpose of this tutorial, this
40//! should be sufficient.
41//!
42//! Now we can observe that an SCC of vertex `v` is always the intersection of forward and backward
43//! reachable vertices from `v`. Hence, we can write the following simple algorithm:
44//!
45//! ```rust
46//! # use biodivine_lib_param_bn::symbolic_async_graph::{SymbolicAsyncGraph, GraphColoredVertices};
47//! # use biodivine_lib_param_bn::BooleanNetwork;
48//! # use std::convert::TryFrom;
49//! # use biodivine_lib_param_bn::biodivine_std::traits::Set;
50//! # fn fwd(graph: &SymbolicAsyncGraph, initial: &GraphColoredVertices) -> GraphColoredVertices {
51//! #     let mut result = initial.clone();
52//! #     loop {
53//! #         let post = graph.post(&result);
54//! #         if post.is_subset(&result) {
55//! #             return result;
56//! #         } else {
57//! #             result = result.union(&post);
58//! #         }
59//! #     }
60//! # }
61//!
62//! # fn bwd(graph: &SymbolicAsyncGraph, initial: &GraphColoredVertices) -> GraphColoredVertices {
63//! #     let mut result = initial.clone();
64//! #     loop {
65//! #         let post = graph.pre(&result);
66//! #         if post.is_subset(&result) {
67//! #             return result;
68//! #         } else {
69//! #             result = result.union(&post);
70//! #         }
71//! #     }
72//! # }
73//! fn scc(graph: &SymbolicAsyncGraph) -> Vec<GraphColoredVertices> {
74//!     let mut universe = graph.mk_unit_colored_vertices();
75//!     let mut components = Vec::new();
76//!     while !universe.is_empty() {
77//!         // Pick one vertex in universe for every color in universe
78//!         let pivots = universe.pick_vertex();
79//!         let fwd = fwd(graph, &pivots);
80//!         let bwd = bwd(graph, &pivots);
81//!         let scc = fwd.intersect(&bwd);
82//!         universe = universe.minus(&scc);
83//!         components.push(scc);
84//!     }
85//!     components
86//! }
87//! // Boolean network from the previous tutorial:
88//! let bn = BooleanNetwork::try_from(r"
89//!     A -> B
90//!     C -|? B
91//!     $B: A
92//!     C -> A
93//!     B -> A
94//!     A -| A
95//!     $A: C | f(A, B)
96//! ").unwrap();
97//! let stg = SymbolicAsyncGraph::new(&bn).unwrap();
98//!
99//! // Note that since the symbolic graph contains different transitions for different colors,
100//! // this will create SCCs that overlap for some colors but are completely different for others.
101//! // Hence, the same vertex can appear in multiple SCCs for different colors.
102//! let components = scc(&stg);
103//! assert_eq!(7, components.len());
104//! assert_eq!(2.0, components[0].vertices().approx_cardinality());
105//! assert_eq!(1.0, components[1].vertices().approx_cardinality());
106//! assert_eq!(2.0, components[2].vertices().approx_cardinality());
107//! assert_eq!(1.0, components[3].vertices().approx_cardinality());
108//! assert_eq!(2.0, components[4].vertices().approx_cardinality());
109//! assert_eq!(2.0, components[5].vertices().approx_cardinality());
110//! assert_eq!(1.0, components[6].vertices().approx_cardinality());
111//! ```
112//!
113//!