Axe's DAG data structure

Axe can represent an ACL2 term in a compact form called a "DAG" (directed acyclic graph). In a DAG, each distinct subterm is represented only once, so DAGs can be much smaller than their corresponding terms. Certain classes of terms with extensive sharing of subterms can be exponentially larger than their DAG representations. Most of Axe's algorithms operate directly on DAGs (often stored internally as arrays).

A DAG contains a set of nodes, each of which has a node number (a natural number) and a "dag-expr" (DAG expression). A dag-expr is either:

- A variable (a symbol), or
- A quoted constant, or
- The application of a function symbol (almost always a defined ACL2 function) to a list of arguments. Each argument (or "darg" = "DAG argument") should be either a quoted constant or the number of a DAG node, which must be smaller than the number of the node that contains this expression. Since the expression for a given node can only refer to nodes with smaller numbers, the DAG is acyclic.

A DAG is usually represented as an alist from node numbers to their corresponding expresspions. Nodes are listed in decreasing order, with each node number consed onto its expression. Here is an example DAG containing 5 nodes:

((4 foo 1 3) (3 bar '2 2) (2 . y) (1 binary-* '2 0) (0 . x)).

The variables in this DAG are

(foo (binary-* '2 x) (bar '2 y)).

This can be seen by calling the function

(dag-to-term '((4 foo 1 3) (3 bar '2 2) (2 . y) (1 binary-* '2 0) (0 . x)))

but note that the term representation can blow up for DAGs with extensive sharing!

The function

(dag-info '((4 foo 1 3) (3 bar '2 2) (2 . y) (1 binary-* '2 0) (0 . x)))

prints:

(DAG info: Unique nodes: 5 2 Variables: x y Functions and counts: binary-*: 1 bar: 1 foo: 1)

Often Axe stores DAGs in named constants, since the DAGs themselves may be large. For example, if the DAG above were stored in the constant

(dag-to-term *my-dag*)

and

(dag-info *my-dag*).