dd
dd copied to clipboard
Binary Decision Diagrams (BDDs) in pure Python and Cython wrappers of CUDD, Sylvan, and BuDDy
About
A pure-Python (3 and 2) package for manipulating:
- Binary decision diagrams (BDDs).
- Multi-valued decision diagrams (MDDs).
as well as Cython bindings to the C libraries:
- CUDD (also read the introduction, and note that the original link for CUDD is http://vlsi.colorado.edu/~fabio/CUDD/)
- Sylvan (multi-core parallelization)
- BuDDy
These bindings expose almost identical interfaces as the Python implementation. The intended workflow is:
- develop your algorithm in pure Python (easy to debug and introspect),
- use the bindings to benchmark and deploy
Your code remains the same.
Contains:
- All the standard functions defined, e.g., by Bryant.
- Dynamic variable reordering using Rudell's sifting algorithm.
- Reordering to obtain a given order.
- Parser of quantified Boolean expressions in either TLA+ or Promela syntax.
- Pre/Image computation (relational product).
- Renaming variables.
- Zero-suppressed binary decision diagrams (ZDDs) in CUDD
- Conversion from BDDs to MDDs.
- Conversion functions to
networkxandpydotgraphs. - BDDs have methods to
dumpandloadthem using JSON, orpickle. - BDDs dumped by CUDD's DDDMP can be loaded using fast iterative parser.
- Garbage collection that combines reference counting and tracing
If you prefer to work with integer variables instead of Booleans, and have
BDD computations occur underneath, then use the module
omega.symbolic.fol
from the omega package.
If you are interested in computing minimal covers (two-level logic minimization)
then use the module omega.symbolic.cover of the omega package.
The method omega.symbolic.fol.Context.to_expr converts BDDs to minimal
formulas in disjunctive normal form (DNF).
Documentation
The changelog is in
the file CHANGES.md.
Examples
The module dd.autoref wraps the pure-Python BDD implementation dd.bdd.
The API of dd.cudd is almost identical to dd.autoref.
You can skip details about dd.bdd, unless you want to implement recursive
BDD operations at a low level.
from dd.autoref import BDD
bdd = BDD()
bdd.declare('x', 'y', 'z', 'w')
# conjunction (in TLA+ syntax)
u = bdd.add_expr(r'x /\ y') # symbols `&`, `|` are supported too
# note the "r" before the quote, which signifies a raw string and is
# needed to allow for the backslash
print(u.support)
# substitute variables for variables (rename)
rename = dict(x='z', y='w')
v = bdd.let(rename, u)
# substitute constants for variables (cofactor)
values = dict(x=True, y=False)
v = bdd.let(values, u)
# substitute BDDs for variables (compose)
d = dict(x=bdd.add_expr(r'z \/ w'))
v = bdd.let(d, u)
# as Python operators
v = bdd.var('z') & bdd.var('w')
v = ~ v
# quantify universally ("forall")
u = bdd.add_expr(r'\A x, y: (x /\ y) => y')
# quantify existentially ("exist")
u = bdd.add_expr(r'\E x, y: x \/ y')
# less readable but faster alternative,
# (faster because of not calling the parser;
# this may matter only inside innermost loops)
u = bdd.var('x') | bdd.var('y')
u = bdd.exist(['x', 'y'], u)
assert u == bdd.true, u
# inline BDD references
u = bdd.add_expr(r'x /\ {v}'.format(v=v))
# satisfying assignments (models):
# an assignment
d = bdd.pick(u, care_vars=['x', 'y'])
# iterate overal all assignments
for d in bdd.pick_iter(u):
print(d)
# how many assignments
n = bdd.count(u)
# write to and load from JSON file
filename = 'bdd.json'
bdd.dump(filename, roots=[u])
other_bdd = BDD()
roots = other_bdd.load(filename)
print(other_bdd.vars)
To run the same code with CUDD installed, change the first line to:
from dd.cudd import BDD
Most useful functionality is available via methods of the class BDD.
A few of the functions can prove handy too, mainly to_nx, to_pydot.
Use the method BDD.dump to write a BDD to a pickle file, and
BDD.load to load it back. A CUDD dddmp file can be loaded using
the function dd.dddmp.load.
A Function object wraps each BDD node and decrements its reference count
when disposed by Python's garbage collector. Lower-level details are
discussed in the documentation.
For using ZDDs, change the first line to
from dd.cudd_zdd import ZDD as BDD
Installation
pure-Python
From the Python Package Index (PyPI) using the
package installer pip:
pip install dd
Locally:
pip install .
For graph layout, install also graphviz.
The dd package remains compatible with Python 2.7,
except for few places where Python 3 is required.
Cython bindings
Wheel files with compiled CUDD
As of dd version 0.5.3, manylinux2014_x86_64
wheel files are
available from PyPI for some Python
versions. These wheel files contain the module dd.cudd with the CUDD
library compiled and linked.
If you have a Linux system and Python version compatible with one of the
available wheels, then pip install dd will install dd.cudd, so you need
not compile CUDD. Otherwise, see below.
dd fetching CUDD
By default, the package installs only the Python modules.
You can select to install any Cython extensions using
the setup.py options:
--cudd: build module of CUDD BDDs--cudd_zdd: build module of CUDD ZDDs--sylvan: build module of Sylvan BDDs--buddy: build module of BuDDy BDDs
Pass --fetch to setup.py to tell it to download, unpack, and
make CUDD v3.0.0. For example:
pip download dd --no-deps
tar xzf dd-*.tar.gz
cd dd-*/
python setup.py install --fetch --cudd --cudd_zdd
The path to an existing CUDD build directory can be passed as an argument:
python setup.py install --cudd="/home/user/cudd"
If you prefer defining installation directories, then follow Cython's instructions
to define CFLAGS and LDFLAGS before running setup.py.
You need to have copied CuddInt.h to the installation's include location
(CUDD omits it).
If building from the repository, then first install cython. For example:
git clone [email protected]:tulip-control/dd
cd dd
pip install cython # not needed if building from PyPI distro
python setup.py install --fetch --cudd
The above options can be passed to pip too, using the --install-option
in a requirements file, for example:
dd >= 0.1.1 --install-option="--fetch" --install-option="--cudd"
The command line behavior of pip is currently different, so
pip install --install-option="--fetch" dd
will propagate option --fetch to dependencies, and so raise an error.
User installing build dependencies
If you build and install CUDD, Sylvan, or BuDDy yourself, then ensure that:
- the header files and libraries are present, and
- suitable compiler, include, linking, and library flags are passed,
either by setting environment variables
prior to calling
pip, or by editing the filedownload.py.
Currently, download.py expects to find Sylvan under dd/sylvan and built with Autotools
(for an example, see .github/workflows/main.yml).
If the path differs in your environment, remember to update it.
Scripts are available that fetch, build, and install the Cython bindings:
Licensing of the compiled modules dd.cudd and dd.cudd_zdd in the wheel
These notes apply to the compiled modules dd.cudd and dd.cudd_zdd that are
contained in the wheel file on
PyPI (namely the files dd/cudd.cpython-39-x86_64-linux-gnu.so and
dd/cudd_zdd.cpython-39-x86_64-linux-gnu.so in the *.whl file, which can
be obtained using unzip).
These notes do not apply to the source code of the modules
dd.cudd and dd.cudd_zdd.
The source distribution of dd on PyPI is distributed under a 3-clause BSD
license.
The following libraries and their headers were used when building the modules
dd.cudd and dd.cudd_zdd that are included in the wheel:
The licenses of Python and CUDD are included in the wheel archive.
Cython does not add its license to C code that it generates.
GCC was used to compile the modules dd.cudd and dd.cudd_zdd in the wheel,
and the GCC runtime library exception
applies.
The modules dd.cudd and dd.cudd_zdd in the wheel dynamically link to the:
- Linux kernel (in particular
linux-vdso.so.1), which allows system calls (see the kernel's fileCOPYINGand the explicit syscall exception in the fileLICENSES/exceptions/Linux-syscall-note) - GNU C Library (glibc) (in particular
libpthread.so.0,libc.so.6,/lib64/ld-linux-x86-64.so.2), which uses the LGPLv2.1 that allows dynamic linking, and other licenses. These licenses are included in the wheel file and apply to the GNU C Library that is dynamically linked.
Installing the development version
For installing the development version of dd from the git repository,
an alternative to cloning the repository and installing from the cloned
repository is to use pip for doing so:
pip install https://github.com/tulip-control/dd/archive/main.tar.gz
or with pip using git
(this alternative requires that git be installed):
pip install git+https://github.com/tulip-control/dd
A git URL can be passed also to pip download,
for example:
pip download --no-deps https://github.com/tulip-control/dd/archive/main.tar.gz
The extension .zip too can be used for the name of the archive file
in the URL. Analogously, with pip using git:
pip download --no-deps git+https://github.com/tulip-control/dd
Note that the naming of paths within the archive file downloaded from
GitHub in this way will differ, depending on whether https:// or
git+https:// is used.
Tests
Use pytest. Run with:
cd tests/
pytest -v --continue-on-collection-errors .
Tests of Cython modules that were not installed will fail. The code is covered well by tests.
License
BSD-3, see file LICENSE.