1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
//! # 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: //! //! ```rust //! 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: //! //! ```rust //! # use biodivine_lib_param_bn::symbolic_async_graph::{SymbolicAsyncGraph, GraphColoredVertices}; //! # use biodivine_lib_param_bn::BooleanNetwork; //! # use std::convert::TryFrom; //! # 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); //! # } //! # } //! # } //! 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()); //! ``` //! //!