I am working on a program correction/synthesis project. My task is to obtain an error trace (counter-example), locate it in the complete state space, and repair the model at that location. I want to implement this as a NuSMV extension.
I have been debugging NuSMV to understand and explore its source code. So far, I have found my way to the creation of the BDD FSM (compile.c line 520). I am trying to find a way to traverse the bdd in order to gain programmatic access to the state space, and thus perform my corrective work upon the model. I have not yet been able to understand the recursive exploration functions NuSMV uses to verify properties via the bdd fsm.
I would like to know how i can traverse the bdd structure so I can visualise it through tools like dot. I would also like to know if such or smilar visualisations have already been made (I have searched but come up empty). Secondly, I would like to verify whether the direction I have taken up is the right one, or if there is a better way to obtain the complete state space of a given model, and explore it, especially with regards to a counter-example obtained through NuSMV.
This is an answer about ways to work with binary decision diagrams (BDDs) using CUDD other than via NuSMV, so focusing on the question's second part.
About symbolic algorithms for investigating a state space, the paper "Algorithmic verification of linear temporal logic specifications" by Kesten, Pnueli, and Raviv (ICALP '98, DOI: 10.1007/BFb0055036) is a good introduction, which covers counterexample construction.
One possibility for visualizing BDDs is working in Python, using Cython bindings to CUDD:
This approach uses
dd, which can be installed usingpip, withpip install dd. Documentation can be found here. Looking at the (internal) moduledd._abcmay also be informative (this serves as a specification; the name "abc" alludes to abstract base classes in Python). (Pure Python suffices for smaller problems, and CUDD is useful for larger problems).There are two kinds of traversals relevant to the question:
These are discussed separately below.
Traversing the graph of a BDD
Depth-first traversal is more common than breath-first when working with BDDs. For the interface of
dd.cuddanddd.autorefsuch a traversal is:Complemented edges need also to be taken into account when working with BDDs (using CUDD or similar libraries). The attribute
u.negatedprovides this information.The function
dd.bdd.copy_bddis a pure Python example of traversing a BDD. This function manipulates BDDs directly through a "low level" interface that is wrapped bydd.autorefto look the same asdd.cudd.Traversal of a state graph
The script
dd/examples/reachability.pyshows how to compute from which states a given set of states can be reached in a finite number of steps.The package
omegais more convenient thanddfor developing BDD-based algorithms related to system behaviors. The scriptomega/examples/reachability_solverdemonstrates a reachability computation usingomega.A basic example of forward reachability using
omega == 0.3.1follows:Comparing
omegawithdd:omegasupports variables and constants, and integer values for these (the relevant module isomega.symbolic.temporal). Variables represent state changes, for examplexandx'. Constants remain unchanged through a system's behavior.ddsupports only Boolean-valued variables (omegauses Boolean-valued variables to represent integer-valued variables, and so represent predicates as BDDs viadd; the bitblasting is done in the moduleomega.logic.bitvector).Several fixpoint operators are implemented in
omega.symbolic.fixpoint. These operators can be used for model checking or temporal synthesis. The moduleomega.logic.pastincludes translations of temporal operators that are relevant to symbolic model checking (also known as temporal testers).Documentation of
omegacan be found here.Further comments
I used the term "step" above to refer to a pair of consecutive states that represents a change of state allowed by the specification. The TLA+ language, and steps, flexible and rigid variables, and other useful concepts are described in Leslie Lamport's book Specifying Systems.
A collection of formal verification software is listed at https://github.com/johnyf/tool_lists/.
In my experience, working at the Python level, with only the BDD manager at the C level, is an efficient approach that leads to readable code, and clearer algorithms.