Package: mdd
Py-MDDPython abstraction around Binary Decision Diagrams to implement
Multi-valued Decision Diagrams.Table of ContentsAboutInstallationUsageVariables, Interfaces, and EncodingsBDD Encoding DetailsOrderingDecisionDiagramsConverting to Directed Graph (networkx)AboutMulti-valued Decision Diagrams (MDD)  are a way to represent discrete function:f : A₁ × A₂ × … × Aₙ → B.Conceptually, a MDD forfcan be thought of as a compressed decision tree (in the form of a directed acyclc graph).For example, if we have a function over two variables,x ∈ {1,2,3,4,5}, y ∈ {'a','b'}with possible outputsf(x, y) ∈ {-1, 0, 1}, then the following diagram represents the function:f(x, y) = 1 if (x ≡ 1 and y ≡ 'a') else 0This library provides abstractions to easily create and manipulate MDDs.InstallationIf you just need to usepy-mdd, you can just run:$ pip install mddFor developers, note that this project uses thepoetrypython package/dependency
management tool. Please familarize yourself with it and then
run:$ poetry installUsageFor the impatient, here is a basic usage example:importmddinterface=mdd.Interface(inputs={"x":[1,2,3],"y":[6,'w'],"z":[7,True,8],},output=[-1,0,1],)func=interface.constantly(-1)assertfunc({'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).assertfunc.bdd.dag_size==33If 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]Variables, Interfaces, and EncodingsThemddapi centers around three objects:Variable: Representation of a named variable taking on values in
from a finite set described by an aiger circuit.Interface: Description of inputs and outputs of a Multi-valued Decision Diagram.DecisionDiagram: Representation of a Multi-valued Decision Diagram that conforms
to an interface. This object is a wrapper around a Binary Decision Diagram object (fromdd).By default, variables use one-hot encoding, but allinputvariables can use arbitrary encodings by defining a bit-vector
expression describingvalidinputs and a encode/decoder pair fromints to the variable's domain.# One hot encoded by default.var1=mdd.to_var(domain=["x","y","z"],name="myvar1")# Hand crafted encoding using `py-aiger`.importaiger_bv# Named 2-length bitvector circuit.bvexpr=aiger_bv.uatom(2,'myvar3')domain=('a','b','c')var2=mdd.Variable(encode=domain.index,# Any -> intdecode=domain.__getitem__,# int -> Anyvalid=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 anaiger_bvBitVector object to describe circuits in terms of a variable.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 aDecisionDiagram:Given an interface, create a constant function:func=interface.constantly(1)Wrap anpy-aigercompatible object:importaiger_bvasBVx=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)assertfunc({'x':2,'y':6,'z':True})==0assertfunc({'x':1,'y':6,'z':True})==-1Wrap 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.assertfunc({'x':2,'y':6,'z':True})==0assertfunc({'x':1,'y':6,'z':True})==-1Partially Evaluate an existingDecisionDiagram:constantly_0=func.let({'x':2})assertfunc({'y':6,'z':True})==0Override an existingDecisionDiagramgiven 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)assertfunc2({'x':1,'y':6,'z':True})==1BDD Encoding DetailsThepy-mddlibrary 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:If a variable encoding is invalid, then the bdd maps it to0.The output is 1-hot encoded, i.e., there is a variable for each
outcome.If abddhas all input variables fixed to a valid assignment, the
resultingbdddepends on exactly one output varible, which then
determines the output.Any bdd that conforms to this encoding can be wrapped up by an
approriateInterface.OrderingDecisionDiagramsThe underlying BDD can be reordered to respect variable ordering by
providing a complete list of variable names to theordermethod.func.order(['x','y','z',func.output.name])Converting to Directed Graph (networkx)If thenetworkxpython package is installed:$ pip install networkxor thenxoption is added when installingpy-mdd:$ pip install mdd[nx]then one can export aDecisionDiagramas 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.frommdd.nximportto_nxgraph=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.