Proofs: Italiano's dynamic transitive closure algorithm
These are the human proofs of Italiano's dynamic transitive closure algorithm, at full length.
Italiano's reachability trees stay exact under insertions, and under deletions in a DAG full
This proof shows: correctness.
Write for the set of vertices reachable from in the current graph, with . The invariant is:
(T) For every , is a tree rooted at with vertex set , and every tree edge from a parent to a child is an edge of the graph.
If (T) holds, line L5 is correct, and line L6 returns a path of graph edges from to , because following parents from in a rooted tree ends at the root. For the empty graph, every tree is the single vertex , and (T) holds.
Insertions
Lemma 1. Let be with the new edge , and the reachable sets in . If , then . Otherwise .
Proof. A path in that does not use is a path in . A path from to that uses reaches in before its first use, so , and after its last use it is a path in from to , so . So when , and when no path from reaches , since then the new edge is never used. The reverse inclusion is clear.
Note that does not change in the insertion when it matters: by Lemma 1 with , if , so in all cases. Line L2 never picks , because , so is not changed while it is read.
Lemma 2. If (T) holds before INSERT, then it holds after it.
Proof. Fix . If , then and by Lemma 1, and is not changed. If , then , since reachability is transitive, so again and nothing changes.
Otherwise line L3 walks from . We claim that after the walk the vertex set of is . Every vertex added is in , so in . Conversely let . Its path in from is . No is in : otherwise , which is reachable from by tree edges, would be in by transitivity. So none of was in before the walk. The walk reaches first. When the walk reaches , it is not yet in , because each vertex of the tree is reached only from its parent, at most once; so it is added and its children, including , are pushed. By induction, is added. Pruned vertices are in already, and every vertex is added at most once since the walk skips vertices already present.
Each added vertex gets as parent a vertex already in : for , and otherwise its parent in , which was added just before was pushed. So the parents point to earlier vertices, and is still a tree rooted at . The new tree edges are , a graph edge, and edges of , graph edges by (T). With Lemma 1, the vertex set is , and (T) holds for .
Deletions in a DAG
Now the graph is acyclic, and is a topological numbering: for every edge . Edges are only deleted, so stays a topological numbering, and reachable sets only shrink. For deletions we add a second invariant.
(P) For every and every , the pointer of in is at its tree parent , the edge is present, and every in-edge of before the pointer either is deleted or comes from a vertex .
The construction sets every pointer at the first present in-edge from a vertex of , so (P) holds at the start. The resulting parent pointers go from larger to smaller , so they form a tree rooted at , and (T) holds.
Lemma 3. If (T) and (P) hold before DELETE, then they hold after it.
Proof. Let be the new reachable sets. Fix . If is not the parent of in , the tree uses no deleted edge, so it is still a tree of graph edges and spans a set of vertices reachable in the new graph; since , it spans . The pointers do not move, and the edges before them still satisfy (P), since sets only shrink. So assume .
Call a vertex settled when it is taken from and processed. We prove, by induction in the order of processing, that when is taken from :
- (a) every vertex with that is marked reachable in is in , and every vertex not marked reachable is not in ;
- (b) is hooked again if and only if .
The vertices put into are and the children of removed vertices. Each child has a larger number than its parent, and returns the least number, so the numbers taken from increase. A vertex is marked not reachable when it enters , and its descendants keep their marks until their own turn.
For (a): a vertex with that is marked reachable is either settled and hooked, so in by (b) for , or it was never an orphan. In the second case its whole tree path from consists of vertices that were never orphans, since an orphan's descendants all become orphans until the orphan is hooked again, and a hooked orphan is in . So its tree path uses only present edges and . A vertex marked not reachable is either outside , and so outside , or it was settled and removed, so outside by (b).
For (b): the in-neighbors of have , so by (a) the test of line L9 is exactly . If , a path from to in the new graph ends with a present edge with . That edge is at or after the pointer: every edge before it, by (P), was deleted or came from a vertex outside . So line L9 finds an edge and hooks . If , every present in-edge comes from a vertex outside , so no edge is found and is removed. Every edge passed by the pointer is deleted or comes from outside , so (P) holds for afterwards.
When is empty, every orphan has been settled. A vertex of that never became an orphan keeps its tree path, which uses present edges, so it is in . Every vertex of lies below the deleted tree edge, so it became an orphan and was removed by (b). So the marked vertices are exactly . The parents of hooked vertices have smaller numbers and are in , so the parent pointers again form a tree rooted at of present edges. Hence (T) and (P) hold.
Theorem. Under insertions in any digraph, and under deletions in a DAG, every query REACHABLE returns whether , and PATH returns a path of graph edges from to when there is one.
Proof. By induction on the updates, (T) holds after each insertion by Lemma 2, and (T) and (P) hold after each deletion by Lemma 3. Queries read , which by (T) has vertex set and consists of graph edges.