Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
14 views

A Java Library For ZDD

Uploaded by

tartar2002
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views

A Java Library For ZDD

Uploaded by

tartar2002
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 57

Institute of Software Technology

Reliable Software Systems

University of Stuttgart
Universitätsstraße 38
D–70569 Stuttgart

Bachelorarbeit Nr. 306

A Java Library For


Zero-Suppressed Decision
Diagrams
Robert Golda

Course of Study: Informatik

Examiner: Dr.-Ing. André van Hoorn (Prof.-Vertr.)

Supervisor: Sinem Getir

Commenced: February 1, 2016

Completed: August 2, 2016

CR-Classification: E.2
Abstract

On the one hand, decision diagrams are a common means to represent Boolean functions.
Since they were first proposed in the 80s, they became more and more popular. They
are widely used in circuit optimization and verification and are useful when dealing
with combinatorial problems. Zero-Suppressed Decision Diagram (ZDD)s are one type
of those diagrams. On the other hand, Java is presently one of the most popular
programming languages. Much research was done on each of these two subjects, ZDDs
and Java, separately, but not on a combination of both. There is no appropriate library for
Java implementing ZDDs. In the course of this thesis such a library was developed and
implemented. The thesis describes the basic functionality of decision diagrams and lists
particularities regarding the realization of them. Finally, the results of benchmarks for
the library in general and the optimizations in particular are presented and discussed.

3
Kurzfassung

Einerseits sind Entscheidungsdiagramme ein weit verbreitetes Mittel, um Boolsche Funk-


tionen darzustellen. Seit sie in den 80er-Jahren erstmals vorgeschlagen wurden, wurden
sie immer populärer. Sie werden häufig in Schaltkreisoptimierung und -verifikation
eingesetzt und sind nützlich bei kombinatorischen Problemen. ZDDs sind eine Art
dieser Diagramme. Andererseits ist Java heutzutage eine der beliebtesten Program-
miersprachen. Viele Arbeiten wurden jeweils über ZDDs und Java durchgeführt, aber
nicht über der Vereinigung dieser beiden Forschungsgebiete.Es gibt keine angemessene
Bibliothek für Java, die ZDDs implementiert. Eine solche Bibliothek wurde im Verlauf
dieser Arbeit entwickelt und implementiert. Die Arbeit beschreibt die grundlegenden
Funktionen von Entscheidungsdiagrammen und nennt Besonderheiten diesbezüglich.
Zum Schluss werden die Ergebnisse der Benchmarks für die Bibliothek im Allgemeinen
und die Optimierungen im Besonderen präsentiert und diskutiert.

5
Contents

1 Introduction 17
1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.2 Goals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.3 Document Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

2 Preliminary Work 19
2.1 Papers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

3 Theory 21
3.1 Term Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.2 Binary Decision Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.3 Zero-Suppressed Decision Diagrams . . . . . . . . . . . . . . . . . . . . 29
3.4 Comparison . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

4 Implementation 35
4.1 Attributed Nodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.2 Cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.3 Garbage Collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

5 Evaluation 43
5.1 Benchmarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
5.2 General Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
5.3 Effects of Optimizations . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

6 Conclusion and Future Work 53

Bibliography 55

7
List of Figures

3.1 Binary Decision Diagram (BDD) Node Deletion Rule . . . . . . . . . . . 28


3.2 Node Sharing Rule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.3 ZDD Node Deletion Rule . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.4 Efficient Construction of a ZDD . . . . . . . . . . . . . . . . . . . . . . . 31
3.5 A Full Tree with universe size |U | = n + 1 . . . . . . . . . . . . . . . . . 32

4.1 UML class diagram of the thesis library’s essential parts . . . . . . . . . . 36

5.1 A Petry net of a Milner scheduler . . . . . . . . . . . . . . . . . . . . . . 45


5.2 Histogram of run times for 12 queens on integer level . . . . . . . . . . . 45
5.3 Histogram of run times for 12 queens on object level . . . . . . . . . . . 46
5.4 Histogram of run times for 12 queens with JDD . . . . . . . . . . . . . . 46
5.5 Comparison of run times for different instances of the n queens problem 47
5.6 Histogram of run times for 20 milner on integer level . . . . . . . . . . . 48
5.7 Comparison of run times for different instances of the milner scheduler . 48
5.8 Space requirements of the milner scheduler . . . . . . . . . . . . . . . . 49
5.9 Usefulness of the cache for the n queens benchmark . . . . . . . . . . . 50
5.10 Usefulness of the cache for the milner benchmark . . . . . . . . . . . . . 50
5.11 Usefulness of the attributed nodes for the n queens benchmark . . . . . 51
5.12 Usefulness of the garbage collection for the 12 queens benchmark . . . . 52
5.13 Usefulness of the garbage collection for the 12 queens benchmark . . . . 52
5.14 Usefulness of the garbage collection for the n queens benchmark . . . . 52

9
List of Tables

3.1 Comparison of special cases between BDD and ZDD . . . . . . . . . . . . 32

5.1 Average run times of the n queens benchmark in milliseconds . . . . . . 47


5.2 Average run times of the milner benchmark in milliseconds . . . . . . . . 48

11
List of Acronyms

BDD Binary Decision Diagram


GC Garbage Collection
ZDD Zero-Suppressed Decision Diagram

13
List of Algorithms

4.1 Example for integer level . . . . . . . . . . . . . . . . . . . . . . . . . . . 37


4.2 Example for object level . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

15
Chapter 1

Introduction

1.1 Motivation

BDDs nowadays are used in a wide area of applications. One prominent field is the
hardware synthesis and hardware verification. In former days, the circuits were not so
large that they couldn’t be handled by BDDs, but as downscaling progresses, the circuits
grow larger. Many frequently used algorithms have a compact representation of their
circuit as a BDD, but some important functions, e.g. the multiplication, can only be
represented using exponential space. This also leads to exponential processing times
of this data structures. Therefore, it becomes more and more necessary to be able to
formally represent these circuits efficiently in regard to both, time and space. Several
propositions were published to tackle this problem. There are on the one hand the
Boolean Expression Diagrams (BED), which are a generalization of BDDs, and on the
other hand the Zero-Suppressed Decision Diagrams (ZDD), which are the topic of this
work and use another set of reduction rules than the BDDs. However, the number of
libraries for ZDDs isn’t as high as for BDDs by far.

1.2 Goals

Up to now, several libraries for a few programming languages were published to handle
the new data structures and the respective algorithms. They may meet their design
targets, but lack some general requirements. For example in the Java programming
language, there is only one notable library capable of handling ZDDs, which has some
flaws. Another library worth mentioning is written in the C programming language.

17
1 Introduction

Recently, the Java language becomes more and more popular in industrial as well as
academical areas. So, there are frequently cases where the programmer is required to
use Java as the programming language. This leaves him with only two choices. He
can use either the flawed Java library or he can use a port from Java to the C library,
keeping the data structure in the C program and issuing many procedure calls from Java
to C. Here, the port consumes much time which is spent unproductively. Both solutions
are unsatisfactorily inefficient. Therefore, this work aims at developing a library for
the Java programming language which makes such ports or use of inefficient libraries
unnecessary. Case studies are conducted, evaluated and their results presented.

1.3 Document Structure

This thesis is separated in five main chapters. In section two, an overview over the
preliminary work is presented, including both, papers and the libraries mentioned above.
After that, the theoretical background of the data structures and algorithms are explained
in section 3. Firstly, decision diagrams are defined formally, beginning with BDDs, and
then ZDDs are explained in comparison to the BDDs. Section 4 tells about the process
of the implementation, elaborates some particularities and names and justifies some
design decisions. Finally, the process of the evaluation is explained and then its results
are presented accompanied by several diagrams and depictions in Section 5. In the end,
a conclusion is drawn and possible future work is sketched.

18
Chapter 2

Preliminary Work

2.1 Papers

Decision diagrams as a means to represent Boolean functions were first proposed


by Bryant [1] in 1986. He introduced the graph structure and the associated algo-
rithms for the manipulation of them. He showed that, while in theory the size of
the graph is exponential depending on the number of arguments in the worst case,
most functions met in practice have a much more manageable size. Additionally, these
BDDs are canonical, which means that equivalent Boolean functions share the same
diagram. In combination with algorithms with a time complexity proportional to the
number of nodes in the graph, this yields an efficient way to represent Boolean functions.

Since then much research was done which built upon his ideas. In 1998, Andersen
[2] introduced a generalized derivation of BDDs, called Boolean Expression Diagrams
(BED). For efficiency’s sake, they can represent a Boolean function in linear rather than
exponential space complexity, but in return give up the canonicity of the representation.
Therefore, a Boolean function doesn’t have any minimal BED representation.

Another type of decision diagrams are the ZDDs, which are the topic of this thesis. They
were introduced by Minato [10] in 1993. They also aim at more efficiency than ordinary
BDDs, but not at the cost of losing their canonicity. As a result, ZDDs are indeed more
efficient in representing certain classes of Boolean functions, whereas for some other
classes, they perform worse than normal BDDs.

A year later Minato [12] extended the usefulness of this new data structure when he
proposed to use ZDDs to represent cube sets rather than Boolean functions. These can

19
2 Preliminary Work

be mapped to ZDDs more directly, just like Boolean functions to BDDs. He also proposed
algorithms necessary to perform cube set operations on ZDDs.

The field of use cases for ZDDs was widened by Coudert [7] in 1997, who introduced
a theoretical framework for solving optimization problems on graphs using ZDDs. He
showed how three different so-called primitive problems could be solved using ZDDs,
and reduced several other well-known graph problems to one of these primitives, ren-
dering them solvable efficiently, too.

One year later, further algorithms for the manipulation of ZDDs were developed and
published by Okuno [14] in 1998. He focused on ZDDs representing sets of combinations
to solve Constraint Satisfaction Problems (CSP). Operations on such sets can be reduced
to a series of ordinary ZDD operations, but regarding their frequent use, a more compact
and efficient algorithm was necessary.

2.2 Libraries

The number of libraries implementing the creation and manipulation of BDDs grows
more and more. Any interested user has a wide range of offers to choose from. For
ZDDs however, the possibilities are limited. Most libraries supporting the use of ZDDs
are actually BDD libraries extended by some ZDD functionality, and even those are scarce.

There are two major libraries implementing Zero-Suppressed Decision Diagrams. CUDD
is a library written in the C programming language and was developed at the University
of Colorado. It features all important operations on ZDDs, but misses some efficiency
functions such as the combination set operations introduced by Okuno. For ease of use
it also provides an object-oriented C++ wrapper.

There is also a library written in the Java programming language called JDD, which
implements most operations needed. Nevertheless, the implementations of some meth-
ods are not very efficient. Additionally, it takes only integer values as input for both,
variables and whole ZDDs, which means that there is no such thing like type security.
In a way, the user could hand over an integer representing a variable as a ZDD integer,
which would result in unexpected results. He could knowingly or unknowingly input
arbitrary values by giving hard-coded values as parameters to the methods. This would
lead to undefined behavior.

20
Chapter 3

Theory

3.1 Term Definition

In this section we define and clarify the terms and vocabulary we use in the following
sections.
Definition 3.1.1
A Boolean Variable is a variable with two possible values: f alse (0) and true (1). They
are called Boolean values.
In combination with the negation operator ¬ each variable a has two literals: ¬a and a.

With this, we can define a Boolean Function.


Definition 3.1.2
A Boolean Function f : {0, 1}n → {0, 1} maps a Boolean variable a 7→ {0, 1} or a vector
of variables →

a 7→ {0, 1} to the set containing the two Boolean values.
The entirety of a function’s input variables is called universe or simply U. Consequently the
number of variables can be denoted as |U | = n.

This leads to the definition of an assignment.


Definition 3.1.3
An Assignment determines the Boolean value of each of a Boolean function’s input variables.

If there are two assignments yielding true which differ only in one variable, they can be
merged into one assignment. The variable which constituted the difference is said to
hold the value don’t-care in the new assignment. That means it doesn’t matter whether
it holds the value true or f alse, the new assignment evaluates to true in both cases.

21
3 Theory

With assignments, we can define a normal form.


Definition 3.1.4
The Canonical Disjunctive Normal Form (CDNF) or Canonical Sum of Products
(CSoP) is a normal form for Boolean functions. This form is a disjunction of all as-
signments yielding true. Each assignment is formulated as a conjunction of all variables in
the universe with the respective values.

The advantage of a normal form in general is that equivalent Boolean functions share
the same CDNF. These forms can often be further simplified, but especially in their
unsimplified form, they correspond with the concept of combinations. Combinations
come in two models: unate and binate.
Definition 3.1.5
A unate combination is a set containing all variables which were assigned the Boolean
value true in an assignment yielding true.
A binate combination is a set containing for each variable in the function’s universe either
its positive literal if it has the value true in the respective assignment or its negative literal
otherwise.
Combinations are also called cubes.

We see that a conjunction of the unsimplified CDNF can be directly mapped to a binate
cube by inserting each of its literals into this set. We can obtain an unate cube by
inserting only the conjunction’s positive literals in the set.

There is an essential difference in the semantics of the absence of a variable between


these two paradigms. In unate cube sets, there are only positive literals, which means a
negative value for a variable can only be represented by the variable’s absence in the
cube. But binate cube sets can contain positive as well as negative literals. Therefore
a not-present variable means this variable’s value is not of interest. In this case the
variable is said to have the don’t-care value.
We illustrate this correlation with an example. Let f be a simple Boolean function with
two input variables:

f (a, b) = a ∨ b

We find three different assignments for this equation which yield true. They are

f (true, true), f (true, f alse), f (f alse, true)

22
3.1 Term Definition

Now we can calculate the CDNF as a disjunction of those assignments. The example’s
function in its (unreduced) normal form looks like this:

f (a, b) = (a ∨ b) ∧ (a ∨ ¬b) ∧ (¬a ∨ b)

Consequently, the respective unate combinations are

{a, b}, {a}, {b}

and the binate combinations are

{a, b}, {a, ¬b}, {¬a, b}

This leads automatically to the next definition.


Definition 3.1.6
A set of combinations represents a Boolean function. It contains a combination for each
of this function’s assignments yielding true.
Those sets are also called cube sets.

In an abbreviated way, this concept of sets of combinations corresponds to the CDNF.


Knowing the function’s universe, one can obtain the DNF by building a conjunction of
the disjunction from each of the combinations. In the case of a unate cube set there is
the positive literal in the conjunction if a variable is present in the combination. Other-
wise, the conjunction contains the variable’s negative literal. In the case of binate cube
sets, the conjunction contains exactly the same literals as the corresponding combination.

As a continuation of the example above, the function can also be represented by the
unate set of combinations

{{a, b}, {a}, {b}}

To make these annotations more readable, we omit the brackets and commas of the
inner sets under the constraint that the variable’s names are not longer than one single
letter. The cube set then looks like this:

{ab, a, b}

Now that we have a basis on Boolean algebra, we introduce the cube set algebra. This
algebra works on sets, whose elements represent Boolean variables.

23
3 Theory

Definition 3.1.7
The Cube Set Algebra is a set of rules about how to perform calculation with or manipulate
sets of combinations.

There are several important operations defined analogous to their application in other
algebras, e.g. on natural or real numbers. Furthermore, there are two operations without
an analogue in ordinary algebra, but which are necessary due to the cubes’ nature as
sets. These are the intersection operation and the change operation. The entirety of the
operations can be divided in two groups regarding how they handle their parameters’
cubes. Methods in the first group see the cubes as atomic set elements. They compare
them with one another, but they never modify them. Cubes in the results are always
contained in one or both input parameters. The methods of the first group behave
exactly like ordinary set operations. P and Q are two cube sets and c is a cube in these
sets.
• Addition / Union
P + Q = {c|(c ∈ P ) ∨ (c ∈ Q)}
• Intersection
P ∩ Q = {c|(c ∈ P ) ∧ (c ∈ Q)}
• Subtraction / Difference
P − Q = {c|(c ∈ P ) ∧ (c ∈
/ Q)}
Results from operation in the second group may contain cubes not contained in any of
the input cube sets. They actively create new ones.
• Change
Changes the Boolean value of a given variable in all cubes of a given cube set. I.e.
if a unate cube contains the variable, it is deleted from the cube, otherwise it is
added.
• Multiplication
Generates all possible concatenations of the cubes of both input cube sets, i.e.
unites each cube from P with each cube from Q and inserts the result in the
solution cube set.
• Division
The result of this operation can be described depending on the number of cubes in
the second cube set or the divisor. If there is only one cube, then in the solution
there are all cubes from the dividend which contained all variables in the divisor’s
cube, with those variables removed.
If there are multiple cubes, this operation is performed for each cube in the

24
3.1 Term Definition

divisor and the final solution can be obtained by calculating the intersection of all
intermediate results.
• Remainder
The remainder or modulo operation is the fourth operation in this group and is
the result of the formula P %Q = P − (P/Q) ∗ Q
Just like algebra on natural numbers this algebra has neutral elements for addition and
multiplication. The neutral element for the addition or union is the empty set {}, which
contains no cube. Analogous to natural number algebra, this set is also denoted as 0.
Therefore the following equation holds.

{} = 0

The neutral element of the multiplication is written {{}} or 1. Thus we have the
equation

{{}} = {1} = 1

In contrast to 0, 1 can appear in cube sets. For example the function

f (a, b) = ¬a ∨ ¬b

has the cube set

{a, b, {}}

Note that the {} in this cube set is an empty cube, not the empty cube set 0. To prevent
misunderstandings, the empty cube is sometimes denoted as 1. Due to that, the cube set
above could also look like this

{a, b, 1}

There are some differences between these operations for unate and binate cube sets.
E.g. in binate algebra, the result of the multiplication operation should not contain any
cubes with both, a positive and a negative literal of the same variable, as such cubes
always evaluate to f alse and are therefore irrelevant in the DNF. Their definitions are
still the same, so the following example will only cover unate cube sets.

Let P and Q be two unate cube sets.

P = {ab, bd, acd}


Q = {a, d}

25
3 Theory

The division is the set of all cubes containing a combination in the divisor, with this
combination then removed from each cube.

P/Q = P/{a} ∩ P/{d}


P/Q = {b, cd} ∩ {b, ac}
P/Q = {b}

The multiplication is each possible concatenation of cubes.

(P/Q) ∗ Q = {b} ∗ Q
(P/Q) ∗ Q = {ab, bd}

Now, the only thing missing to obtain the remainder is calculating the difference between
this result and P .

P %Q = P − (P/Q) ∗ Q
P %Q = P − {ab, bd}
P %Q = {acd}

We can verify this calculation by summing up the result of the remainder operation with
the result from the division operation multiplied with Q. If the results equals P , the
calculations where correct.

(P %Q) + ((P/Q) ∗ Q) = {acd} + {ab, bd}


(P %Q) + ((P/Q) ∗ Q) = {acd, ab, bd}
(P %Q) + ((P/Q) ∗ Q) = P

3.2 Binary Decision Diagrams

Now we introduce Binary Decision Diagrams (BDD). They were developed as an efficient
graphical representation of Boolean function. They are formally defined via the IF-THEN-
ELSE operator.
Definition 3.2.1
The IF-THEN-ELSE operator is a ternary operation written as IT E(a, f0 , f1 ). It evaluates
the first parameter a. If it yields f alse, the whole operator has the value of f0 , otherwise it
has the value of f1 .

26
3.2 Binary Decision Diagrams

Of course, this operator can be represented using only the operations from ordinary
Boolean algebra. The following equation holds for arbitrary functions.

IT E(a, f0 , f1 ) = (¬a ∧ f0 ) ∨ (a ∧ f1 )

In the case that f0 and f1 again are composed of ITE operators, this implies a recursive
calculation. I.e. all Boolean functions can be described in a recursive manner by
decomposing them one variable after the other. In more complex functions, the results
of an ITE operator are again two ITEs with further ITEs as results again, and so on for
arbitrarily many more hierarchies. The decomposition of functions into ITE operators is
called Shannon Expansion or Shannon Decomposition.
Definition 3.2.2
The Shannon Decomposition or Shannon Expansion breaks Boolean functions down to
a series of ITE operations according to a specific order of variables as the operators’ first
parameters.

We can again take the equation f (a, b) = a ∨ b from the example above to clarify how the
Shannon expansion works. First, an order relation of the variables has to be defined. In
most cases we can simply use the alphabetic ordering for variables named with letters.
Therefore, a < b is true in our example. Variables with a higher index are decomposed
first. The first ITE operation is consequently

f (a, b) = IT E(b, a, 1)

We see that if b evaluates to true, the value of a is irrelevant, otherwise the value of f is
the same as the value of a. The next step of the expansion is the trivial expansion of a,
as this is the last variable in our order.

f (a, b) = IT E(b, 1, IT E(a, 0, 1))

Now, because all variables have been expanded, the Shannon Decomposition is complete.
We see that in the end, there are no more Boolean algebra operation in the function, but
only the variables, constants and ITE operators. This maps directly to the BDDs, which
are a graphical representation of Boolean functions after the Shannon Decomposition.

Definition 3.2.3
Binary Decision Diagrams are directed, acyclic graphs G = (V, E) with a set of vertices
V and a set of edges E. The vertices represent either a variable in the respective function’s
universe or a primitive Boolean value.
If the variables’ order is determined, the BDD is called an ordered BDD or OBDD and the

27
3 Theory

a a

1 0 1 0

Figure 3.1: BDD Node Deletion Rule

edges point only from larger variables to smaller variables in the given order.
If two reduction rules were applied to the OBDDs exhaustively, they are called reduced
OBDDs or ROBDDs.

In the following, we call ROBDDs simply BDDs for readability’s sake. If unreduced
or unordered decision diagrams are meant, this will be explicitly mentioned. These
two reduction rules are essential for the usefulness of BDDs, as they can decrease the
diagrams’ complexity greatly. A further advantage is that the equivalence of two Boolean
function can now simply be checked by testing if they have the same BDD. We introduce
these reduction rules in the following.

The Node Deletion Rule is applied each time a source node’s two outgoing edges point to
the same target node. In this case, the source node can simply be deleted and all edges
which pointed to the source node will now point to the target node. An illustration can
be seen in Figure 3.1. In this and the following Figures we assume that the left outgoing
edge is the 0-edge and the right outgoing edge is the 1-edge.

The Node Sharing Rule can be applied if there are two nodes in the graph representing the
same variable and whose 0-edges and 1-edges point to the same node respectively. One
of those nodes can then be deleted and all incoming edges now point to the remaining
identical node, as shown in Figure 3.2.
BDDs are a useful representation of Boolean functions, but we should keep in mind that
they have a space complexity of O(|U |2 ). The reduction rules help here and can reduce
some BDDs to sizes in O(|U |) or even O(1), but not all.

28
3.3 Zero-Suppressed Decision Diagrams

b b

a a a

1 0 1 0

Figure 3.2: Node Sharing Rule

b b

1 0 1 0

Figure 3.3: ZDD Node Deletion Rule

Due to the direct correspondence of BDDs with Boolean functions, they can be built in
the same way as these functions, using the Boolean algebra operations ¬, ∨ and ∧ in
the same priority as defined by the mathematical rules.

3.3 Zero-Suppressed Decision Diagrams

Zero-Suppressed Decision Diagrams are a variation of the Binary Decision Diagrams


explained above. They use a different set of reduction rules to change their behavior and
their efficiency representing certain types of Boolean functions or sets of combinations.
They achieve these changes by keeping the same node sharing rule as BDD, but by using
a different form of the node deletion rule. In the case of ZDDs, nodes can be deleted or
skipped if their 1-out-edge points to the constant-0 leaf node as shown in Figure 3.3.
One advantage of this reduction rule follows implicitly from the ZDDs’ property that
variables with the value f alse in an assignment do not appear in the respective path. If

29
3 Theory

the variable has this value in all present paths, it doesn’t appear at all in the diagram.
Thus, one can assume that the cube sets universe is infinite, containing all variables,
under the premise that these additional variables have the value f alse in all assignments.
In other words, one can add variables at will to the universe, for the added variable was
part of the universe all along.

Of course, this way of thinking only works for ZDDs representing sets of combinations.
Applied to Boolean functions, this would change the functions’ truth tables completely.
We illustrate this connection with an example. Let’s take the function f represented by
the cube set

{a0 }

Under the assumption of




 
a = a0

the corresponding Boolean function would be

f (→

a ) = a0

However, if we add an additional variable, some problems arise. To the cube set we
could add a variable a1 under the premise that it has the value f alse in this assignment.
Our parameter vector would then have the form
!

− a
a = 0
a1

Nothing would change in the cube set representation. But adding this variable to a
Boolean function would make adjustments necessary. The function would change to

f (→

a ) = a0 ∧ ¬a1

To add variables to a Boolean function, we would have to use BDDs and add them under
the premise that they have the value don’t-care in all assignments. Of course, Boolean
functions can nevertheless be represented by ZDDs, but not in an as intuitive way as by
BDDs.

30
3.3 Zero-Suppressed Decision Diagrams

b change(a)
a
change(b)

1 1

0 1 0 1

union union

b b
0

0 1 a 1

0 1
Figure 3.4: Efficient Construction of a ZDD

Another difference to BDDs is the way a ZDD is built efficiently. This includes the
prevention of intermediate ZDDs larger than the final one. As we can presume that the
represented function is sparse, we build the ZDD starting from the constant-0 node. We
then add the few needed cubes with the manipulations presented in the section about
unate cube sets rather than with the ordinary Boolean function operators. Let

f (a, b, c) = a¬b¬c ∨ ¬ab¬c

be our function for this example. To create the ZDD, we build the respective cubes with
the change operation from the primitive constant-1 ZDD and then add this set with the
union operator to the solution diagram. The process is illustrated in Figure 3.4. We
can also observe that the size of the universe doesn’t have an impact on the size of the
ZDDs as long as all further variables have also the value f alse in all conjunctions, just
like c. In contrast to that, building the ZDD with the Boolean operations would lead
to intermediate results with a size complexity proportional to the size of the universe,
where the cube set construction has a constant complexity. An efficient construction
as a BDD would need more operations, because then we would perform all Boolean
operations used in the formula.

31
3 Theory

an ... a 0 1

Figure 3.5: A Full Tree with universe size |U | = n + 1

Function BDD ZDD


f (→
−a)=0 constant-0 constant-0


f( a ) = 1 constant-1 full tree
fj (→
−a ) = aj aj -node full tree


f( a ) = −
V
linear linear
a ai



f( a ) = −
W
→a linear full tree
a i
fj (→

a ) = aj ∧ − a ,i̸=j ¬ai linear aj -node
V



f( a ) = −
L
full tree linear
→a ai

Table 3.1: Comparison of special cases between BDD and ZDD

3.4 Comparison

These two forms of decision diagrams only differ in one reduction rule, but there are
nevertheless great differences regarding the respective semantics of the resulting graphs.
The comparison in this subsection ignores the node sharing rule in its description of the
diagrams, as both types share this rule.

Some special cases represent very different Boolean functions. The diagram consist-
ing only of the constant-1 leaf node represents the function f (→ −x ) = 1, where every
assignment yields true, if one reads it as a BDD. But read as a ZDD, the same graph
is a representation of the function f (→

x ¬xi , where only the assignment, which
V
x)= − →

assigns each variable in the universe the value f alse, yields true. Here, we can see the
large drawback regarding ZDDs. To represent the same function as the constant-1 BDD
in the example above, a ZDD needs the full tree. In this diagram the two out-edges from
each node point to nodes representing the next variable in the given order or to the
constant-1 leaf node, if this node’s variable is the smallest in that order.

Nevertheless such full trees are not as inefficient as they may seem. Thanks to the node
sharing rule which can be applied to both, BDDs and ZDDs, their space complexity can
be reduced from the polynomial O(|U |2 ) to the linear O(|U |). A full tree reduced this
way is displayed in 3.5. Additionally, the concept of a full tree can also be applied to
parts rather than the whole diagram. Thus the "full tree" entries in Table 3.1 mean that
this concept holds true for at least some intervals in the order of the variables.

32
3.4 Comparison

These differences result from the implicit semantics of missing nodes. BDDs delete a
node, if it points with both out-edges to the same node, which means that its variable’s
value doesn’t matter for the evaluation of the function given this path’s assignment.
Therefore, a deleted variable has the value don’t-care. BDDs are reduced in exactly the
same way as Boolean functions. For example in the function

f (a, b) = (¬a ∧ ¬b) ∨ (¬a ∧ b)

We see that the variable b is not necessary. Regardless of b’s value, one of the CDNF’s
conjunctions will evaluate to true, yielding true for the complete function. As a result,
the function could be shortened to

f (a, b) = a

This reduction corresponds to the BDD node deletion rule, where the node representing
b would be removed from the graph. The changes to the diagram are shown in the
graphical example in Figure 3.1. As a consequence, BDDs can be directly derived from
arbitrary Boolean functions. They excel in representing them.

For ZDDs however, there is no reduction analogous to this simplification of the function.
The ZDD reduction rule removes a node if and only if it points to constant-0 with
its 1-out-edge. The changes according to this rule are displayed in Figure 3.3. They
correspond the step in the ZDD construction, when the negative literals are removed
from the sets representing the CDNF’s conjunctions. Thus, a missing node means that its
variable’s value is f alse. Also, one can not build the accompanying ZDD from a Boolean
function without detours. The calculation of an auxiliary structure like the truth table or
the CDNF is necessary. From this, we can obtain the set of combinations representing the
function. The ZDD directly maps to this cube set. The reduction rule itself corresponds
to the mapping between the unsimplified CDNF or the truth table and the cube set,
where the variables of negative literals don’t appear anymore.

In Table 3.1 we can also see that in many cases, BDDs are more efficient in representing
Boolean functions than ZDDs. These are especially the cases were many (fj (→ −
x ) = xj )


or all (fj ( x ) = 1) variables have the value don’t-care in assignments evaluating to true.
This can simply be explained by the reduction rule applied to BDDs. Exactly the nodes
representing these variables can be removed from the graph, resulting in a very small
diagram. In a ZDD, those nodes cannot be removed because ZDDs do not have this
reduction rule. Therefore, ZDDs represent those functions with roughly a full tree with
a space complexity of O(n2 ).

33
3 Theory

The cases ZDDs excel at are Boolean functions where most variables have the value f alse
in assignments yielding true, and which have very few of such assignments. Functions
and their respective cube sets to which these descriptions apply are called sparse. Both
characteristics have different impacts on the ZDD representation of the function. A ZDD
has exactly as many paths leading to a constant-1 node as its Boolean function has
assignments evaluating to true or as its function’s cube set has elements. Therefore, the
breadth of a ZDD grows in proportion to the number of 1-paths. The other characteristic
however has an influence on the tree’s depth. As variables are removed from paths in
the diagram if they have the value f alse in the corresponding assignment, this shortens
the path. If a variable has the value f alse in all 1-paths, this reduces the diagram’s
overall depth. As a result, they are especially useful representing cube sets which in
return are an efficient representation of sparse functions.

As a general rule of thumb, the construction of arbitrary decision diagrams begins with
the respective primitives. Combining those, we can build more and more complex
structures. Therefore, the way we make BDDs and ZDDs differs greatly, because they
have essentially different primitive diagrams. For BDDs, the diagrams are simple if
many variables have the value don’t-care. This statement also holds true for Boolean
functions. BDDs are therefore built in the same way as the corresponding functions.
ZDDs however are simple if as many variables as possible have the value f alse. They in
turn are constructed by first building the individual assignments yielding true and then
combining these cubes to the according set of combinations.

34
Chapter 4

Implementation

For the implementation of a library for the Java programming language, we aimed at
efficiency as a first target and at usability as a second one. This is reflected in the overall
design of the library. There are two main packages which are responsible for one of the
two aims, respectively. The core package implements the pure functionality on a very low
level of abstraction to achieve as much efficiency as possible. The wrapper package pro-
vides a high-level object oriented interface adequate for an Object Oriented Programming
(OOP)-language like Java. Both packages consist of hierarchies of inheritances. These
relationships serve as an implementation of the concept of generalization. Methods and
attributes common to all classes on a certain hierarchy level are generalized. This means
a new class containing all these methods is created and put into a newly inserted level di-
rectly above the level of the classes sharing these methods. All classes then inherit those
methods. With this concept, the amount of repetitive code can be reduced to a minimum.

Additionally, we split some hierarchy levels in order to implement the concept of Sep-
aration of Concerns. Each level solves a different problem. The highest levels of both
hierarchies contain the state of the respective data structures, whereas the second level
contains the common functionality needed to change this state. This was done to increase
the readability and comprehensibility of the code in spite of a minor loss of efficiency.
All levels but the lowest one are implemented as abstract classes, making it impossible to
instantiate them. The user can only use the classes on the lowest level of both hierarchies.
We can see in the UML class diagram that each of the classes on the bottom of the core
hierarchy has a counterpart on the bottom of the wrapper package wrapping it. These
wrapper classes emulate their offered methods by calling the according methods of the
respective core classes, they do not implement any functionality by themselves. Thus, this
architecture can be seen as a two layer model, where the lower level of the core package,
called integer level, provides the functionality in an efficient way and where the object

35
4 Implementation

core::ZddManager core::NodeTable
+union(p: int, q: int): int -uniqueTable: int[][]
+intersect(p: int, q: int): int -hashTable: int[]
+diffrence(p: int, q: int): int -isComplemented: bool[]
-freeListHead: int
+change(root: int, var: int): int -cache: CacheInterface
+offset(root: int, var: int): int *getNode(var: int, p0: int, p1: int): int
+onset(root: int, var: int): int *refNode(root: int): void
*derefNode(root: int): void
+restriction(p: int, q: int): int *checkForGC(): void
+exclusion(p: int, q: int): int
+permission(p: int, q: int): int

core::ZddManagerBinate core::ZddManagerUnate
+mult(p: int, q: int): int +mult(p: int, q: int): int
+div(p: int, q: int): int +div(p: int, q: int): int
+modulo(p: int, q: int): int +modulo(p: int, q: int): int uses / wraps
n
n n

uses / wraps uses / wraps

m m m

wrapper::ZddBinate wrapper::ZddUnate wrapper::ZddBoolFunction


-mgr: ZDDManagerBinate -mgr: ZDDManagerUnate -mgr: ZDDManagerUnate
+and0/1(var: Variable): void +change(var: Variable): void +and(q: ZddAlgebra): void
+factor0/1/D(var: Variable): void +on/offset(var: Variable): void +or(q: ZddAlgebra): void
+not(): void
+multiplication(q: ZddBinate): void +multiplication(q: ZddUnate): void
+division(q: ZddBinate): void +division(q: ZddUnate): void +toDNF(): String
+remainder(q: ZddBinate): void +remainder(q: ZddUnate): void +toCNF(): String

+toCubeSetStr(): String +toCubeSetStr(): String

wrapper::ZddAlgebra wrapper::Zdd
*root: int *root: int
-shutDown: bool -shutDown: bool
+restrict(q: ZddAlgebra): void +getManager(): ZddManager
+exclude(q: ZddAlgebra): void +shutDown(): void
+permit(q: ZddAlgebra): void +isShutDown(): bool

+union(q: Zdd): void union(q: Zdd): void


+intersect(q: Zdd): void intersect(q: Zdd): void
+difference(q: Zdd): void difference(q: Zdd): void

Figure 4.1: UML class diagram of the thesis library’s essential parts

36
Algorithmus 4.1 Example for integer level
int varA = 0, varB = 1;
int zddConst1 = 1, zddConst0 = 0;
int zddA = change(zddConst1, varA);
int zddSol1 = union(zddSol, zddA);
int zddB = change(zddConst1, varB);
int zddSol2 = union(zddSol, zddB);

level called higher layer of the wrapper package provides usability using the OOP concept.

A special case is the class representing Boolean functions, which provides its function-
ality based on unate cube sets rather then based on a separate class. It also doesn’t
inherit from the level directly above but from the level one additional step above.
This is a result from some constraints we need to impose on the underlying sets of
combination to use them to represent Boolean functions properly. Especially the size
of the universe may not grow in the way of cube sets if the cube set represents a function.

To allow the user to choose his view on the ZDDs he works on, the layers use different
signatures for their methods, leading to two ways of manipulating ZDDs. On the integer
level, the diagrams are conceptually immutable. They can only be created as several
predefined primitives or based on one or two input ZDDs. Integer values act as references
to these structures. Constructing the ZDD as shown in Figure 3.4 could be done as
shown in Algorithm 4.1.

Of course we could use the same integer variable to refer to different ZDDs in the process
of the algorithm. We allocated a new integer variable for each intermediate ZDD to
emphasize the ZDDs’ immutability.

In contrast to that, on the object level the user only has to initiate one solution ZDD
and change it over and over until it contains the desired combinations. The same
construction would look different, as displayed in Algorithm 4.2.

We see that the ZDD sol runs through several changes. Internally, the wrapper only
emulates its own semantics with the integer levels semantics, but nevertheless the user
can choose the concept he finds more intuitive.

37
4 Implementation

Algorithmus 4.2 Example for object level


Variable varA = createVariable("a"), varB = createVariable("b");
Zdd a = createUnit(), b = createUnit(), sol = createEmptySet();
a.change(varA);
sol.union(a);
b.change(varB);
sol.union(b);

Up to now, the interface to the library was presented. In the following we explain how
the graphs’ nodes are represented internally in the library to implement this interface
efficiently. The subsequent subsections elaborate concepts which further optimize this
implementation. To achieve as much efficiency as possible, the nodes are stored as
a list of primitive integer values internally. Therefore, the library holds an entry in a
two-dimensional integer array for each ZDD node it has to represent. An entry is an
one-dimensional integer array with length 6. For its corresponding node it holds the
following information:
• variable
• target of 0-out-edge
• target of 1-out-edge
• number of pointers with this entry as their target
• successor entry in bucket list
• predecessor entry in bucket list
The table is organized such that each entry is unique. It is therefore called unique table.
This is especially useful when comparing two ZDDs, as this means that we only need
to look at both ZDDs’ entries in the table. If they are equal, then the ZDDs are equal,
too. Also, it reduces the overall space needed, as the number of equivalent ZDDs doesn’t
have an impact on the space all those ZDDs need in total.

To achieve this uniqueness, we have to be able to find a certain entry in the table in a
short time. For this, we use hashing. As the resolution for the collisions occurring in
hashing we choose closed rather than open addressing, because when we do Garbage
Collection (GC), we want to be able to delete entries, i.e. mark them as free. Doing
so in a hash table with open addressing would destroy the system and would lead
to undefined behavior. Therefore, we have to store for each entry its successor and
predecessor in its bucket list to implement closed addressing.

38
A further necessity when implementing hashing with external references is a hash table,
which maps an entry’s hash value to it’s index in the unique table, because we cannot
changes all external references after each rehashing. As the hash function depends
on the length of the hash table, an entry’s hash value changes if the hash table is
resized due to the need for more space. With a table for such a mapping, we can
now make use of two advantages. We have a table which we can resize at will, but
without changing the entries’ indices. Pointers to individual nodes point to an entry in
the unique table. The hash table is only used to find certain nodes there to keep it unique.

These two tables grow completely independently from another. Each has a condition
to trigger such a growth. The unique table increases its size if there are more nodes to
handle than there is space in the table. The case of resizes for the hash table is a bit
more complex. As the efficiency of the implemented hashing depends also on the length
of the bucket lists, a growth of the hash table is linked to a bounded length of all bucket
lists. If one of these lists exceed this limit, the hash table is resized and all entries in the
unique table are rehashed. This reduces the average length of all bucket lists, as there
are now more bucket lists in total.

In the unique table we separate the entries in several classes. This is used by the GC and
the allocation of new entries in particular. These classes are
• permanent entries
• volatile entries
• free entries
• unused entries
The library keeps track of the highest index ever used in the unique table. All entries
above this index are called unused. All below are in one of the other three classes.
Referenced entries are called permanent and persist through GC. The reference has to
be placed explicitly by the user, if he works on the integer level. If he doesn’t do so
or removes all references to an entry, it is called temporary. In this case, it can further
be used just like permanent nodes and is part of a bucket list until the GC is called.
After that, it will be marked as a free entry and may be returned after a request for the
allocation of a new node at any time.

Together with the Boolean data structure for the attributed nodes that makes a total
of 6 integer values and 1 Boolean value per node. This sums up to approximately 25
Bytes per node, assumed that Java needs one Byte to represent a Boolean variable.
Additionally, all entries with the same hash value share one integer in the hash table.

39
4 Implementation

Together with the cache, this is the entirety of data structures kept in storage by the
library itself. This information is then stored in an additional array.

4.1 Attributed Nodes

Many operations have special cases, which could terminate in constant time O(1) if the
method could find out in constant time if its parameters constitute such a case. In our
implementation, we annotate to nodes the information whether their respective subtree
has the unit set as a subset, i.e. has a path to the constant-1 node using only 0-out-edges.
The operation processing the calculation

{abc, cde, 1} ∨ {1}

could return instantly without the need to traverse any of the trees. This is achieved by
a recursive definition: The constant-1 node contains the unit set per definition. At the
instantiation of each node it is checked if its 0-out-edge points to a node containing the
unit set. If true, the instantiated node also contains the unit set.

4.2 Cache

A particularity of the node sharing rule presented in the Binary Decision Diagram
subsection is that it reduces only the ZDD’s space, but not its time complexity. Even in a
completely with this rule reduces tree (e.g. f (→ −
a ) = 1 as a ZDD), there is still a number
|U |
of paths from the root to any leaf in O(2 ). But often time is more crucial than space.
We want an implementation for which processing such diagrams has a time complexity
of O(|U |), just like its space complexity. This can be achieved by using a cache, because
even if there are many paths crossing the same node, those paths use the same subtree
starting at that node. Therefore, the recursive methods for those paths called on this
node have the same result. Thus, when we perform the calculation for the first time, we
store the result in our cache. Subsequent calculations can check if their result is already
contained in the cache. If so, they can simply take this value and return it in constant
time O(1). The size of the cache is determined based on the number of variables. As
the maximum size of a ZDD depends on this number, we have therefore a cache large
enough to store all or most of the possibly contained subtrees. This is a condition for the
cache to work properly.

40
4.3 Garbage Collection

4.3 Garbage Collection

Especially on the integer level we can see the significance of GC. If we change one single
integer variable as reference from one to another ZDD, we don’t want the former one
lying around unreferenced until the program terminates and occupying space otherwise
better used. To tackle this problem, the implementation has a GC functionality. As
always with GC, a decision has to be made in the trade off between frequency and
effectiveness. Calling the GC over and over leads to the most efficient use of space at the
cost of time. On the other hand never calling GC saves much time but requires a high
overhead regarding the needed space. There are three feasible points in the execution
of the code for calls to the GC (in descending order of frequency)
1. After each operation
2. After the space freed in the last GC is consumed again
3. After the maximum required space was increased
The third point is a special case as its maximum needed space grows proportional to the
size of the processed ZDDs as well as to the elapsed time. That makes this point quite
unattractive for GC. Using one of the other two points, the maximum needed space is
only dependent on the size and number of handled ZDDs.

The first point also has a disadvantage worth mentioning. Calculations yielding the
same result may appear in different, subsequent operations. As the GC needs to clear
the cache when it’s called, such calculations cannot make use of the cache if GC was
active between their respective operations, even though this would further reduce the
processing time. As a result of the trade off, GC is executed at the second point.

41
Chapter 5

Evaluation

In this section we present the results of the evaluation of the implementation. Two
benchmarks were used to test and verify the library. They were conducted on a machine
running Windows 10 and with an Intel Pentium CPU with 2 Cores and 2.13GHz and
with 4GB RAM. We begin with a short description of the benchmarks.

5.1 Benchmarks

The n queens problem is a theoretical problem from the mathematical branch of combi-
natorics. The task is to place n chess pieces, the queens, on an n × n-chess board so that
no queen can capture any other queen. This benchmark was implemented using and
therefore testing the basic cube set operations. The solution is calculated in the following
way. For each square on the board, we define a variable whose Boolean value denotes if
there is a queen. xy,z represents the z th square in the y th row. Thus, a cube may only
contain exactly n elements. With that, the resulting decision diagram can be considered
sparse and therefore the use of ZDDs is efficient. We calculate correct solutions for
a growing part of the board, beginning with the first row and then adding one row
after the other in their order. We denote these intermediate solutions Si , i ∈ {1, 2, .., n}.
Therefore, S1 contains all correct solution for placing one queen in the first row, S2 all
correct solutions for placing two queens in the first two rows and so on. Finally, in Sn ,

43
5 Evaluation

there are all solutions for the whole problem. The calculation of these solutions can be
seen below. This algorithm was proposed by Minato [11].

S1 = x1,1 + x1,2 + .. + x1,n


S2 = x2,1 ∗ (S1 %x1,1 %x1,2 ) + x2,2 ∗ (S1 %x1,1 %x1,2 %x1,3 ) + .. + x2,n ∗ (S1 %x1,n−1 %x1,n )
S3 = x3,1 ∗ (S2 %x2,1 %x1,1 %x2,2 %x1,3 )
+ x3,2 ∗ (S2 %x2,1 %x2,2 %x1,2 %x2,3 %x1,4 )
+ ..
+ x3,n ∗ (S2 %x2,n−1 %x1,n−2 %x2,n %x1,n )
S4 = ...

The second benchmark calculates the state space of a scheduler as defined by Milner
[15]. The n processes are scheduled according to the following rules:
• The process pi cannot be activated before process pi−1 was activated.
• A process can only be activated if it isn’t active at the moment.
• After the activation of pn , process p1 can be activated again.
A Petri net modeling this scheduler is shown in Figure 5.1. The benchmark was,
in contrary to the one for the n queens problem, implemented using the advanced
operations restrict and exclude as defined by Okuno [14]. Thus, the benchmark starts
with a full tree as the solution and creates several ZDDs representing constraints. These
are then applied to the solution ZDD using Okuno’s operators. Formally, we represent
the places in the Petri net with variables. Therefore, our universe has the size 3 ∗ n.
These variables are accessed using predicates on the elements of the set of processes P .
Two types are used: Constraints which have to be satisfied by the solution

∀p ∈ P : active(p) ∨ inactive(p)
∃p ∈ P : cycle(p)

where the first one says that each process is either active or inactive and the second one
says that at least one of the processes is the next to be activated. Additionally, there are
constraints which mustn’t be satisfied by the solution.

∀p ∈ P : ¬(active(p) ∧ inactive(p))
∀p, q ∈ P, p ̸= q : ¬(cycle(p) ∧ cycle(q))

Here, the first one ensures that no process is both, active and inactive. The second one
guarantees that there is always at most one process that can be activated.

44
5.2 General Results

activate0 cyclen activaten


cycle0

inactive0 active0 inactiven activen

deactivate0 deactivaten

Figure 5.1: A Petry net of a Milner scheduler

Figure 5.2: Histogram of run times for 12 queens on integer level

After the positive constraints are applied to the solution using the restrict operation and
the negative constraints using the exclude operator, the solution ZDD contains all states
the Petri net can arrive at starting from the given state.

5.2 General Results

We begin with the results for the n queens benchmark. As representatives for the other
cases, Figures 5.2 and 5.3 show the run times for the queens benchmark with 12 squares
per side on the integer and object level, respectively. Figure 5.4 shows the results
using the JDD library. We see that the distribution of the values is quite small. The
difference in time between the runs which needed the most and the least time amounts
to approximately 5% of the average value. As expected, this applies to all cases, because
the algorithms do not include any randomized parts. The differences may therefore only

45
5 Evaluation

Figure 5.3: Histogram of run times for 12 queens on object level

Figure 5.4: Histogram of run times for 12 queens with JDD

originate in varying scheduling behavior of the operating system.

We also see that our implementation is faster than the JDD library on both levels. This
observation holds true for all problem instance sizes above a certain threshold, which
lies between the instances with 10 and 11 squares per row, as shown in Figure 5.5 and
the corresponding Table 5.1. Before this threshold, the JDD implementation was slightly
faster. This is probably due to some overhead work needed by our implementation, e.g.
for the construction of the data structure for the annotated nodes optimization, which
isn’t implemented by JDD. Nevertheless the overhead’s impact on the run time shrinks
more and more with the growth of the instance size.

The same observation applies to the results using our implementation on the object level.
As expected, they need more time than the solution on integer level in all cases, but
they become faster than the JDD library at some point just as well. A problem regarding
Figure 5.5’s clarity is the difference in complexity between the instances of successive
queens instances. The times needed to solve the 8 queens problem and the 12 queens
problem differ by many magnitudes. Even successive instances have a large difference.

46
5.2 General Results

Figure 5.5: Comparison of run times for different instances of the n queens problem

8 9 10 11 12
Integer 18,71 56,32 186,75 1001,03 7074,74
Object 60,06 142,61 352,80 1328,23 8134,89
JDD 10,09 42,34 221,82 1828,03 15267,17

Table 5.1: Average run times of the n queens benchmark in milliseconds

The benchmark for the milner scheduler doesn’t have this problem. Here, the times
needed for the solution of successive instances are quite similar, as illustrated in Figure
5.7 and Table 5.2. Therefore, we can compare many different sizes of the problem
without the diagram losing clarity. We can see that this benchmark confirms some of the
results obtained with the n queens problem. The solution on the integer level is again
faster than the solution on the object level. Interestingly, the difference between the run
times of the benchmarks on the two levels increases with the growth of the problem size,
whereas it seemed to shrink in the queens benchmark. Nevertheless both solutions are
faster than the JDD library.

Unfortunately, we cannot verify the observation that JDD is faster for small instances.
The system time of Java’s Virtual Machine doesn’t have a resolution high enough for
accurate time measurements in these dimensions. As we can see in Figure 5.6, even with

47
5 Evaluation

Figure 5.6: Histogram of run times for 20 milner on integer level

20 25 30 35 40
Integer 4,38 6,58 8,53 12,56 19,40
Object 17,54 30,96 45,86 70,47 231,65
JDD 27,87 49,56 89,88 130,70 201,24

Table 5.2: Average run times of the milner benchmark in milliseconds

this size of the problem we already have a majority of solutions needing "0" milliseconds
to complete. This means they terminated in a time to small for the system to measure
properly. For the interval of problem sizes used, that isn’t considered a problem, as the
number of such cases is a statement by itself. The more solutions needed 0 milliseconds
to complete, the more efficient is the overall library. But by further reducing the problem
size, the number of such cases increases until they lose even this information when
finally all or nearly all solutions finish in 0 seconds.
The library’s behavior regarding the used space is displayed in Figure 5.8. We see the
discrete steps in the graphs for the two tables’ sizes. They grow when certain conditions

Figure 5.7: Comparison of run times for different instances of the milner scheduler

48
5.3 Effects of Optimizations

Figure 5.8: Space requirements of the milner scheduler

mentioned above are met by some static growth factor. This factor is again the result of
a trade off. In principle, we want as few resizes as possible, which would mean we want
a large resize factor. But if we need space for only one more node and this allocation
causes a new resize, we want the table to grow only by one single entry. Ideally, we
know the required size from the beginning,
√ in which case we don’t need to perform any
resizes. But without this knowledge, 2 provides good time results.

We also see that, with some exceptions, the number of used entries increases propor-
tional to the size of the problem instance while the size of the unique table stays constant
until it would hit the unique table size. Is this case, the unique table also grows by one
step. We can easily see that in the interval between 23 and 26 processes.

The hash table does not grow as predictably. Its size is of course determined without any
probability. Nevertheless, it is some kind of chance how many entries handled by the
library share the same hash value and therefore may cause an (strictly speaking unneces-
sary) resize of the hash table. This also explains exceptions in the hash table’s size like for
the instance with 24 processes. Nevertheless a general growing trend line is recognizable.

5.3 Effects of Optimizations

We also conducted some experiments regarding the various optimizations we used.


Firstly, we have a look at the cache. The effect of this data structure can be seen in
Figure 5.9 and in Figure 5.10. We see that this optimization’s usefulness greatly depends

49
5 Evaluation

Figure 5.9: Usefulness of the cache for the n queens benchmark

Figure 5.10: Usefulness of the cache for the milner benchmark

on the type of the problem and the shape of the handled ZDDs. The ZDDs in the n
queens problem tend to be sparse, therefore there are only few subtrees which occur
multiple times in the same tree. Thus, we only save a little time using the cache. On the
other hand, the impact of this optimization on the run time of the milner benchmark
cannot be underestimated. Here, we have a large tree, where some subtrees occur many
times. As a result, the library can take full advantage of the cache data structure, leading
to huge time savings. This goes so far that benchmarks for instances of the size we
used to compare our library with the JDD library don’t terminate in reasonable time
without a cache. Thus, Figure 5.10 only shows the results for smaller instances. We
can see that the run time grows very fast without cache, while the run time with cache
practically stays constant. Consequently, the cache is absolutely necessary for a library
implementing ZDDs.

50
5.3 Effects of Optimizations

Figure 5.11: Usefulness of the attributed nodes for the n queens benchmark

The tests for the attributed nodes had a similarly obvious result, but lead to a contrary
consequence. If we look at Figure 5.11, we see that this optimization has almost no
effect on the run time of the benchmark. This may result from the fact that it only
constitutes an optimization from O(n) to O(1) for some special cases. Without a table to
directly read the results from in constant time, the library needs to traverse the tree using
0-out-edges only to a leaf node and read this leaf’s value. Therefore, the time saved
using this data structure is insignificant compared with the other calculations, which
have a higher complexity. Thus, the optimization of attributed edges is not necessary.

The third optimization explained in the section above, the garbage collection, mainly has
the task to reduce the library’s needed space. How much it decreases the size in the main
data structure, the unique table, can be seen in Figure 5.12. This happens by deleting
nodes which are not in use anymore. The effects are displayed in Figure 5.13, whose
graphs show a growth behavior similar to the graph displaying the size of the unique
table. The size of the table and the number of its used entries grow significantly faster
without garbage collection. Additionally, because the library doesn’t have to allocate as
much space for its tables, the benchmark is also more efficient regarding time, as shown
in Figure 5.14. This holds true only for large instances, because allocation is cheap for
small instances. For those instances, the cost for additional allocation is outweighed by
the effort to perform garbage collection.
Some works on ZDDs argued that a library without GC "learns" more and more solutions
and therefore gets more and more efficient, because it can find these solutions in its
store or cache in constant time. But our results contradict this claim. We deem garbage
collection absolutely necessary.

51
5 Evaluation

Figure 5.12: Usefulness of the garbage collection for the 12 queens benchmark

Figure 5.13: Usefulness of the garbage collection for the 12 queens benchmark

Figure 5.14: Usefulness of the garbage collection for the n queens benchmark

52
Chapter 6

Conclusion and Future Work

In this thesis we defined zero-suppressed decision diagrams and explained how they
work and where they differ from binary decision diagrams. We presented our imple-
mentation and elaborated some particularities. Finally, we used the library to solve two
benchmarks and discussed the results. With that, we showed that an efficient library
can be implemented in the Java programming language despite the additional layer of
Byte Code and the Virtual Machine. This also applies to interfaces to the library which
don’t give up the object oriented paradigm.

Nevertheless, there is still room for improvements. On the one hand, due to its high
complexity and cost, we didn’t implement variable reordering. Somenzi [16] named
several different ways to achieve a more efficient order in his description of the library
CUDD, but he also noted that some of these operations are often very slow and therefore
don’t yield any time profit.

On the other hand, not all methods are implemented directly. This originated in the
indirect definition of those operations. A direct approach for operations like

A%B = A − (A/B) ∗ B

or

Exclude(F, C) = F − Restrict(F, C)

may save much time. We only need to have a look at the calculation of the solutions
for the n queens problem, to see the importance of such direct implementations. There,
approximately half of the operations are calculations of the remainder, which leads to
three methods calls where one might be enough. We tried to find an algorithm for the %

53
6 Conclusion and Future Work

operator but didn’t succeed.

We also aren’t satisfied with the condition for the resize of the hash table. With the
current approach, the size of this table behaves more or less randomly based on the
length of some individual bucket lists. But keeping track of the length of all bucket lists
seemed like a waste of space. There is also the possibility to calculate the average length
of the bucket lists as a result of a stream of bucket list lengths with the formula
i−1 1
average(→

x , i) = average(→

x , i − 1) ∗ + xi ∗
i i
where i is at least 1 and at most the length of the given vector. The average of all
elements from the first up to the position denoted by i has to be calculated. But with
large i, this approach has problems with the accuracy of the underlying machine in
representing floating point numbers. Eventually the fractions will be rounded to zero. If
there was no resize until then, no resize will be issued anymore. Thus, this approach is
also not satisfactory. A good condition is yet to be found.

54
Appendix

Bibliography

[1] Bryant, Randal E. "Graph-based algorithms for boolean function manipulation."


Computers, IEEE Transactions on, 100.8 (1986): 677-691.
[2] Andersen, Henrik, and Henrik Hulgaard. "Boolean expression diagrams." Logic in
Computer Science, 1997. LICS’97. Proceedings., 12th Annual IEEE Symposium on.
IEEE, 1997.
[3] Andersen, Henrik Reif. "An introduction to binary decision diagrams." Lecture
notes, available online, IT University of Copenhagen (1997).
[4] Rauzy, Antoine. "New algorithms for fault trees analysis." Reliability Engineering
and System Safety, 40.3 (1993): 203-211.
[5] Coudert, Olivier. "Two-level logic minimization: an overview." Integration, the VLSI
journal, 17.2 (1994): 97-140.
[6] Coudert, Olivier, Jean Christophe Madre, and Henri Fraisse. "A new viewpoint on
two-level logic minimization." Design Automation, 30th Conference on, IEEE, 1993.
[7] Coudert, Olivier. "Solving graph optimization problems with ZBDDs." Proceedings
of the 1997 European conference on Design and Test, IEEE Computer Society, 1997.
[8] Jung, Woo Sik, Sang Hoon Han, and Jaejoo Ha. "A fast BDD algorithm for large
coherent fault trees analysis." Reliability Engineering and System Safety, 83.3
(2004): 369-374.
[9] Minato, Shin-ichi. "Fast generation of prime-irredundant covers from binary deci-
sion diagrams." IEICE transactions on fundamentals of electronics, communications
and computer sciences, 76.6 (1993): 967-973.
[10] Minato, Shin-ichi. "Zero-suppressed BDDs for set manipulation in combinatorial
problems." Design Automation, 30th Conference on, IEEE, 1993.

55
[11] Minato, Shin-ichi. "Zero-suppressed BDDs and their applications." International
Journal on Software Tools for Technology Transfer 3.2 (2001): 156-170.
[12] Minato, Shin-ichi. "Calculation of unate cube set algebra using zero-suppressed
BDDs." Proceedings of the 31st annual Design Automation Conference, ACM, 1994.
[13] Mishchenko, Alan. "An introduction to zero-suppressed binary decision diagrams."
URL: http://www. ee. pdx. edu/alanmi/research.htm (2001).
[14] Okuno, Hiroshi G., Shin-ichi Minato, and Hideki Isozaki. "On the properties of
combination set operations." Information Processing Letters, 66.4 (1998): 195-199.
[15] Milner, Robin. "Communication and concurrency" Vol. 84. New York etc., Prentice
hall, 1989.
[16] Somenzi, Fabio. "CUDD: CU Decision Diagram Package Release 3.0.0." (2015).
Declaration

I hereby declare that the work presented in this thesis is


entirely my own and that I did not use any other sources
and references than the listed ones. I have marked all
direct or indirect statements from other sources con-
tained therein as quotations. Neither this work nor
significant parts of it were part of another examination
procedure. I have not published this work in whole or
in part before. The electronic copy is consistent with all
submitted copies.

place, date, signature

You might also like