## ZDDs Defined

We define a ZDD

\[Z\]

to be any directed acyclic graph such that:\[\top\]

(the "TRUE" node), or: \[\bot\]

(the "FALSE" node). \[v\]

. This label need not be unique. \[v\]

. Thus we can omit arrowheads in diagrams because the edge directions can be inferred from the labels. \[\bot\]

node. \[Z\]

an unreduced ZDD, if a HI edge points to \[\bot\]

or the last condition fails to hold.In our first example, the universe

\[U\]

was the letters of the alphabet. More generally, we consider a universe of size \[n\]

, number the elements from 1 to \[n\]

, and refer to an element by this number.The dag rooted at any node in a ZDD is itself a valid ZDD. Thus we expect straightforward recursive descriptions of many ZDD algorithms and properties. Indeed, anyone comfortable with dynamic programming will feel at home with ZDDs.

## Representing a family

Let\[F\]

be a ZDD. Let \[v\]

be its root node. Then:\[v = \bot\]

then there can be no other nodes and \[F\]

represents \[\emptyset\]

, the empty family. \[v = \top\]

then there can be no other nodes and \[F\]

represents the family containing just the empty set: \[\{ \emptyset \}\]

. We call this the unit family, and denote it by \[\epsilon\]

. \[v\]

has two children. Let \[v_0\]

be the LO node, and \[v_1\]

be the HI node. Let \[F_i\]

be the family represented by the ZDD rooted at \[v_i\]

which is known by inductive assumption. Then \[F\]

represents the family \[ F_0 \cup \bigcup_{\alpha\in F_1} \alpha \cup \{v\} .\]

In other words, as in real life, we divide the world between the haves and the have-nots: on the LO branch we have the sets in \[F\]

that donâ€™t contain \[v\]

:\[ F_0 = \{ \alpha : \alpha \in F, v \notin \alpha \} \]

and on the HI branch we have the sets in \[F\]

that do contain \[v\]

, but we remove the \[v\]

before recording them:\[ F_1 = \{ \alpha\setminus\{v\} : \alpha \in F, v \in \alpha \} \]

Some examples:

The above is the family

\[\emptyset \cup \{\emptyset \cup \{2\}\} = \{ \{2\}\}\]

. This is \[e_2\]

, an elementary family. Elementary families are those of the form \[\{\{n\}\}\]

, and denoted by \[e_n\]

.More family photos:

The family

\[\{\emptyset\} \cup \{\emptyset \cup \{2\}\} = \{ \emptyset, \{2\}\}\]

.The family

\[\{ \{2\} \} \cup \{ \emptyset\cup \{1\} \} = \{ \{1\}, \{2\}\}\]

.The family

\[\epsilon \cup \{ \{1\} \cup \{2\} \} = \{ \{1, 2\}\}\]

.## Features

Two ZDDs are identical if and only if the families they represent are identical (exercise). When we refer to a family as a ZDD, we mean the ZDD representing the family, and vice versa.We can enumerate all sets in

\[F\]

lexicographically (exercise).We have

\[|F| = |F_0| + |F_1|\]

, thus we can recursively compute the number of sets in a ZDD. This also allows us to pick out, for instance, the 13th set out of a 42-member family (exercise). Random access is fast, and somewhat analogous to lazy evaluation in programming languages such as Haskell: the ZDD can produce desired family members quickly on demand. Thus any operation we can do on an array of sets, we can do with similar efficiency on a ZDD.We can modify these techniques so that if we weight each element in the universe, after recursively visiting each node, we can find the sets in the family of maximum (or minimum) weight, and statistics such as the mean and standard deviation. We can compute interesting generating functions, such as one from which we can read off the number of sets of a given size. With an array representation of a family, we would have to visit each set. With a ZDD, we only have to visit each node, which is often much smaller.

As with data structures built from trees, we can convert recursive algorithms to iterative bottom-up algorithms. For conciseness we tend towards the former in our exposition, though the latter may be better in practice.