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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
//! A fully symbolic coloured graph representation of the Asynchronous Boolean Network.
//!
//! Internally, this uses the same type of encoding as "normal" `AsyncGraph`, but it uses
//! extra BDD variables to represent network state variables (note the distinction between
//! state and BDD variables).
//!
//! To the end user, this representation is available via three data structures: `GraphVertices`,
//! `GraphColors` and `GraphColoredVertices`. The first two represent just the subsets of $V$
//! and $C$ respectively, while the last is a full subset of $V \times C$.
//!
//! There is a range of methods that enable (or simplify) the conversion between these three
//! types of sets (mainly provided by `GraphColoredVertices` in the form of projections and
//! vertex-wise operations). Additionally, a `VertexSet` can be iterated with `ArrayBitVectors`
//! used to represent the set elements.
//!
//! The types of conversions and convenience methods are mainly motivated by SCC decomposition
//! algorithms - if we need something else in the future, it will be implemented.
//!
//! `SymbolicAsyncGraph` does not implement the `Graph` trait because it was not useful
//! in the fully symbolic context. It provides `post`, `pre`, and similar methods directly.
//!
//! Internally, the representation is maintained by the `SymbolicContext` which maps each
//! BDD variable either to a state variable of the network, or to a parameter stored in some
//! implicit or explicit `FunctionTable`. The BDD variables are ordered in such a way that
//! the network parameters follow the variable which they are most closely related to, but
//! please do not rely on this ordering, it probably will be changed in the future. Overall,
//! by accessing the `SymbolicContext` (via `SymbolicAsyncGraph`) allows implementing almost
//! any custom BDD operations, but it should be used with caution.
//!

use crate::BooleanNetwork;
use biodivine_lib_bdd::{
    Bdd, BddSatisfyingValuations, BddVariable, BddVariableSet, ValuationsOfClauseIterator,
};
use std::iter::Enumerate;

/// **(internal)** Implementing conversion between `FnUpdate` and `BooleanExpression`.
mod _impl_fn_update_from_boolean_expression;
/// **(internal)** Implementation for `FunctionTable` and `FunctionTableIterator`.
mod _impl_function_table;
/// **(internal)** Implement set operations for `GraphColoredVertices`.
mod _impl_graph_colored_vertices;
/// **(internal)** Implement set operations for `GraphColors`.
mod _impl_graph_colors;
/// **(internal)** Implement set operations for `GraphVertices`.
mod _impl_graph_vertices;
/// **(internal)** Utility methods for validation of static constraints on network regulations.
mod _impl_regulation_constraint;
/// **(internal)** Utility methods for `SymbolicAsyncGraph`.
mod _impl_symbolic_async_graph;
/// **(internal)** Implementation of symbolic utility algorithms.
mod _impl_symbolic_async_graph_algorithm;
/// **(internal)** Implement symbolic graph operators (pre/post/...).
mod _impl_symbolic_async_graph_operators;
/// **(internal)** Implementation of the `SymbolicContext`.
mod _impl_symbolic_context;

/// Symbolic representation of a color set.
///
/// Implementation contains all symbolic variables, but state variables are unconstrained.
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct GraphColors {
    bdd: Bdd,
    parameter_variables: Vec<BddVariable>,
}

/// Symbolic representation of a coloured set of graph vertices, i.e. a subset of $V \times C$.
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct GraphColoredVertices {
    bdd: Bdd,
    state_variables: Vec<BddVariable>,
    parameter_variables: Vec<BddVariable>,
}

/// Symbolic representation of a vertex set.
///
/// Implementation contains all symbolic variables, but parameter variables are unconstrained.
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct GraphVertices {
    bdd: Bdd,
    state_variables: Vec<BddVariable>,
}

/// A helper struct that we need in order to make `GraphVertices` iterable. Elements of such
/// iterable set are bitvectors, specifically `ArrayBitVector`.
///
/// Internally, this struct contains a `Bdd` that has all parameter variables fixed to false,
/// so that we only iterate over vertices and can safely disregard colors.
#[derive(Clone)]
pub struct IterableVertices {
    materialized_bdd: Bdd,
    state_variables: Vec<BddVariable>,
}

/// Iterator over graph vertices.
pub struct GraphVertexIterator<'a> {
    iterator: BddSatisfyingValuations<'a>,
    state_variables: Vec<BddVariable>,
}

/// A symbolic encoding of asynchronous transition system of a `BooleanNetwork`.
///
/// Provides standard pre/post operations for exploring the graph symbolically.
#[derive(Clone)]
pub struct SymbolicAsyncGraph {
    network: BooleanNetwork,
    symbolic_context: SymbolicContext,
    // Empty and unit vertex set.
    vertex_space: (GraphColoredVertices, GraphColoredVertices),
    // Empty and unit color set.
    color_space: (GraphColors, GraphColors),
    // General symbolic unit bdd.
    unit_bdd: Bdd,
    // For every update function, stores `x_i != f_i(x)` (used for pre/post).
    // In other words, symbolically this is the set of states where a transition is enabled.
    update_functions: Vec<Bdd>,
}

/// Symbolic context manages the mapping between entities of the Boolean network
/// (variables, parameters, uninterpreted functions) and `BddVariables` used in `bdd-lib`.
///
/// It also provides utility methods for creating `Bdd` objects that match different conditions
/// imposed on the parameter space of the network.
///
/// Note that while this is technically public, it should not be used unless absolutely necessary.
/// Playing with raw `BDDs` is dangerous.
#[derive(Clone)]
pub struct SymbolicContext {
    bdd: BddVariableSet,
    state_variables: Vec<BddVariable>,
    parameter_variables: Vec<BddVariable>,
    explicit_function_tables: Vec<FunctionTable>,
    implicit_function_tables: Vec<Option<FunctionTable>>,
}

/// Function table maps one the table of an uninterpreted function to corresponding `Bdd` variables.
///
/// The main functionality of a `FunctionTable` is that it provides an iterator over
/// pairs of `Vec<bool>` (function input assignment) and `BddVariable`
/// (corresponding symbolic variable).
#[derive(Debug, Clone)]
pub struct FunctionTable {
    pub arity: u16,
    rows: Vec<BddVariable>,
}

/// Iterator over elements of the `FunctionTable`.
pub struct FunctionTableIterator<'a> {
    inner_iterator: Enumerate<ValuationsOfClauseIterator>,
    table: &'a FunctionTable,
}

#[cfg(test)]
mod tests {
    use crate::biodivine_std::traits::Set;
    use crate::symbolic_async_graph::{GraphColoredVertices, SymbolicAsyncGraph};
    use crate::BooleanNetwork;
    use std::convert::TryFrom;

    #[test]
    fn components() {
        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;
        }
        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();
        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());
    }
}