pip install mdd==0.3.3 Copy PIP instructions
Released: Nov 5, 2020
Python abstraction around Binary Decision Diagrams to implement Multivalued Decision Diagrams.
Python abstraction around Binary Decision Diagrams to implement Multi-valued Decision Diagrams.
Table of Contents
DecisionDiagram
Multi-valued Decision Diagrams (MDD) are a way to represent discrete function:
f : A₁ × A₂ × … × Aₙ → B.
Conceptually, a MDD for f can be thought of as a compressed decision tree (in the form of a directed acyclc graph).
f
For example, if we have a function over two variables,
x ∈ {1,2,3,4,5}, y ∈ {'a','b'}
with possible outputs f(x, y) ∈ {-1, 0, 1}, then the following diagram represents the function:
f(x, y) ∈ {-1, 0, 1}
f(x, y) = 1 if (x ≡ 1 and y ≡ 'a') else 0
This library provides abstractions to easily create and manipulate MDDs.
If you just need to use py-mdd, you can just run:
py-mdd
$ pip install mdd
For developers, note that this project uses the poetry python package/dependency management tool. Please familarize yourself with it and then run:
$ poetry install
For the impatient, here is a basic usage example:
import mdd interface = mdd.Interface( inputs={ "x": [1, 2, 3], "y": [6, 'w'], "z": [7, True, 8], }, output=[-1, 0, 1], ) func = interface.constantly(-1) assert func({'x': 1, 'y': 'w', 'z': 8}) == -1 # Can access underlying BDD from `dd` library. # Note: This BDD encodes both the function's output # *and* domain (valid inputs). assert func.bdd.dag_size == 33
If 33 seems very large to you, this is just a constant function after all, note that as the following sections illustrate, its easy to implement alternative encodings which can be much more compact. [0]
The mdd api centers around three objects:
mdd
Variable
Interface
By default, variables use one-hot encoding, but all input variables can use arbitrary encodings by defining a bit-vector expression describing valid inputs and a encode/decoder pair from ints to the variable's domain.
int
# One hot encoded by default. var1 = mdd.to_var(domain=["x", "y", "z"], name="myvar1") # Hand crafted encoding using `py-aiger`. import aiger_bv # Named 2-length bitvector circuit. bvexpr = aiger_bv.uatom(2, 'myvar3') domain = ('a', 'b', 'c') var2 = mdd.Variable( encode=domain.index, # Any -> int decode=domain.__getitem__, # int -> Any valid=bvexpr < 4, # 0b11 is invalid! ) # Can create new variable using same encoding, but different name. var3 = var2.with_name("myvar3") var4 = mdd.to_var(domain=[-1, 0, 1], name='output')
A useful feature of variables is that they can generate an aiger_bv BitVector object to describe circuits in terms of a variable.
aiger_bv
a_int = var2.encode('a') y_int = var1.encode('y') # BitVector Expression testing if var2 is 'a' and var1 is 'y'. expr = (var2.expr() == a_int) & (var1.expr() == y_int)
Given these variables, we can define an input/output interface.
# All variables must have distinct names. interface = mdd.Interface(inputs=[var1, var2, var3], output=var4)
Further, as the first example showed, if the default encoding is fine, then we can simply pass a dictionary inplace of inputs and/or a iterable in place of the output. In this case, a 1-hot encoding will be created using the order of the variables.
interface = mdd.Interface( inputs={ "x": [1, 2, 3], # These are "y": [6, 'w'], # 1-hot "z": [7, True, 8], # Encoded. }, output=[-1, 0, 1], # uuid based output name. )
Finally, given an interface we can create a Multi-valued Decision Diagram. There are five main ways to create a DecisionDiagram:
Given an interface, create a constant function:
func = interface.constantly(1)
Wrap an py-aiger compatible object:
py-aiger
import aiger_bv as BV x = interface.var('x') # Access 'x' variable. out = interface.output # Access output variable. expr = BV.ite( x.expr() == x.encode(2), # Test. out.expr() == out.encode(0), # True branch. out.expr() == out.encode(-1), # False branch. ) func = interface.lift(expr) assert func({'x': 2, 'y': 6, 'z': True}) == 0 assert func({'x': 1, 'y': 6, 'z': True}) == -1
Wrap an existing Binary Decision Diagram:
bdd = mdd.to_bdd(expr) # Convert `aiger` expression to bdd. func = interface.lift(bdd) # bdd type comes from `dd` library. assert func({'x': 2, 'y': 6, 'z': True}) == 0 assert func({'x': 1, 'y': 6, 'z': True}) == -1
Partially Evaluate an existing DecisionDiagram:
constantly_0 = func.let({'x': 2}) assert func({'y': 6, 'z': True}) == 0
Override an existing DecisionDiagram given a predicate:
# Can be a BDD or and py-aiger compatible object. test = x.expr() == x.encode(1) # If x = 1, then return 1, otherwise return using func. func2 = func.override(test=test, value=1) assert func2({'x': 1, 'y': 6, 'z': True}) == 1
The py-mdd library uses a Binary Decision Diagram to represent a multi-valued function. The encoding slighly differs from the standard reduction [1] from mdds to bdds by assuming the following:
0
bdd
Any bdd that conforms to this encoding can be wrapped up by an approriate Interface.
The underlying BDD can be reordered to respect variable ordering by providing a complete list of variable names to the order method.
order
func.order(['x', 'y', 'z', func.output.name])
If the networkx python package is installed:
networkx
$ pip install networkx
or the nx option is added when installing py-mdd:
nx
$ pip install mdd[nx]
then one can export a DecisionDiagram as a directed graph.
note: for now, this graph is only partially reduced. In the future, the plan is to guarantee that the exported DAG is fulled reduced.
from mdd.nx import to_nx graph = to_nx(func) # Has BitVector expressions on edges to represent guards. graph2 = to_nx(func, symbolic_edges=False) # Has explicit sets of values on edges to represent guards.
[0]: To get a sense for how much overhead is introduced, consider the corresponding Zero Suppressed Decision Diagram (ZDD) of a 1-hot encoding. A classic result (see Art of Computer Programming vol 4a) is the ZDD size bounds the BDD size via O(#variables*|size of ZDD|).
[1]: Srinivasan, Arvind, et al. "Algorithms for discrete function manipulation." 1990 IEEE international conference on computer-aided design. IEEE Computer Society, 1990.
0.3.7
Mar 9, 2021
0.3.5
Nov 13, 2020
0.3.4
Nov 5, 2020
0.3.3
0.3.2
0.3.1
Sep 26, 2020
0.3.0
Sep 18, 2020
0.2.0
Sep 8, 2020
0.1.1
Sep 3, 2020
0.1.0
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Uploaded Nov 5, 2020 Source
Source
Uploaded Nov 5, 2020 Python 3
Python 3
Details for the file mdd-0.3.3.tar.gz.
mdd-0.3.3.tar.gz
ba98fed5fc5d53f580229eb7f0100e1db54cfd04fc5311cf0887ea9ea2bd0cc1
e35f92b24845a32351fbd619f6d2c796
4b0f8befff567857be836920ab2644f8614bc2d607c87734ed71fd77c4b86909
See more details on using hashes here.
Details for the file mdd-0.3.3-py3-none-any.whl.
mdd-0.3.3-py3-none-any.whl
6a9d5e6ace9d97399d2b3d1846f99ad9852fe440331b8b9bfa2c0c5f5dbd6d8a
d1597f69d1e11ed9cc20e834e4f4c973
5b7c4606a689d1b6dfdd75781cc7f010e7d9cc88e20d74a509a546d8a372b975
Supported by