Algorithm Search⌘K

Proofs: Breaking the sorting barrier for directed SSSP

These are the human proofs of Breaking the sorting barrier for directed SSSP, at full length.

The bounded multi-source recursion computes all distances full

This proof shows: correctness, termination.

Claim. Let every edge weight be a real number w(u,v)≥0w(u, v) \ge 0. The top call BMSSP(⌈log⁡2(n+1)/t⌉,∞,{s})(\lceil \log_2 (n+1) / t \rceil, \infty, \{s\}) of line L12 terminates, and afterwards d^(v)=δ(s,v)\hat d(v) = \delta(s, v) for every vertex vv, with d^(v)=∞\hat d(v) = \infty exactly when ss does not reach vv. This holds for every choice of the parameters k≥1k \ge 1 and t≥1t \ge 1; the values k=⌊log⁡1/3n⌋k = \lfloor \log^{1/3} n \rfloor and t=⌊log⁡2/3n⌋t = \lfloor \log^{2/3} n \rfloor matter only for the running time.

Keys. Following the total order of paths in Duan et al. (their Assumption 2.1), compare walks by their key: the length, then the number of edges, then the vertex sequence read from the end, lexicographically. We use three facts.

  • (K1) The keys form a total order, and two different walks have different keys.
  • (K2) For walks Q,Q′Q, Q' that end at the same vertex uu, and an edge (u,v)(u, v): key(Q)<key(Q′)\mathrm{key}(Q) < \mathrm{key}(Q') if and only if key(Q+v)<key(Q′+v)\mathrm{key}(Q + v) < \mathrm{key}(Q' + v).
  • (K3) key(Q+v)>key(Q)\mathrm{key}(Q + v) > \mathrm{key}(Q), because the edge count grows by one and w≥0w \ge 0.

Every label d^(v)\hat d(v) is the key of a walk from ss to vv, or ∞\infty. Write d(v)d(v) for the smallest key of a walk from ss to vv. It exists: deleting a cycle from a walk does not increase the length and decreases the edge count, so the minimum is over the finitely many simple paths. The walk PvP_v with key d(v)d(v) is unique by (K1), and by (K2) its prefix that ends at the previous vertex uu is PuP_u. So the paths PvP_v form a tree, and d(v)d(v) starts with δ(s,v)\delta(s, v). A vertex is complete when d^(v)=d(v)\hat d(v) = d(v).

Relaxing (u,v)(u, v) forms the candidate c=d^(u)+w(u,v)c = \hat d(u) + w(u, v), the key of the walk of uu extended by vv, and sets d^(v)←c\hat d(v) \leftarrow c when c≤d^(v)c \le \hat d(v). Labels never increase and never go below dd. If uu is complete and (u,v)(u, v) is the last edge of PvP_v, the relaxation makes vv complete, because then c=d(v)≤d^(v)c = d(v) \le \hat d(v). Conversely, if a relaxation from uu leaves d^(v)=c=d(v)\hat d(v) = c = d(v), then by (K2) uu is complete and is the vertex before vv on PvP_v.

Notation. For a call with bound BB and frontier SS, and a key bb, let U~(b)\tilde U(b) be the set of vertices vv with d(v)<bd(v) < b whose path PvP_v visits a vertex of SS. A call is proper if, when it starts,

  • (P1) d^(x)<B\hat d(x) < B for every x∈Sx \in S, and
  • (P2) every incomplete vertex vv with d(v)<Bd(v) < B has a complete vertex of SS on PvP_v.

We prove that a proper call terminates and returns (B′,U)(B', U) with

  • (R1) B′≤BB' \le B;
  • (R2) every vertex of U~(B′)\tilde U(B') is in UU, and every vertex of UU is complete and in U~(B)\tilde U(B);
  • (R3) B′>min⁡x∈Sd^(x)B' > \min_{x \in S} \hat d(x), where the labels are taken at the start of the call.

The proof is by induction on the level ll. First, two facts about any proper call.

Fact A (cover). In a proper call, every v∈U~(B)v \in \tilde U(B) has a complete vertex of SS on PvP_v. If vv is incomplete this is (P2). If vv is complete, let x∈Sx \in S be on PvP_v. If xx is complete we are done; otherwise d(x)≤d(v)<Bd(x) \le d(v) < B and (P2) gives a complete vertex of SS on PxP_x, which is a prefix of PvP_v.

Fact B (where the relaxations go). Suppose that during a proper call a relaxation from a vertex u∈U~(B)u \in \tilde U(B) gives c≤d^(v)c \le \hat d(v) and c<Bc < B. Then v∈U~(B)v \in \tilde U(B). Indeed d(v)≤c<Bd(v) \le c < B. If vv was incomplete at the start of the call, (P2) puts a vertex of SS on PvP_v. If vv was complete, then c≤d^(v)=d(v)≤cc \le \hat d(v) = d(v) \le c, so uu is the vertex before vv on PvP_v and Pv=Pu+vP_v = P_u + v visits SS.

Lemma 1 (base case, level 0). A proper call with S={x}S = \{x\} runs Dijkstra's algorithm from xx, pushing only candidates below BB, and stops after k+1k + 1 vertices are popped. By (P2), xx is complete: otherwise d(x)≤d^(x)<Bd(x) \le \hat d(x) < B and (P2) would need a complete vertex of S={x}S = \{x\} on PxP_x.

Every popped vertex is complete when it is popped. Suppose uu is the first popped vertex that is not. Then d(u)<d^(u)<Bd(u) < \hat d(u) < B, and by (P2) PuP_u visits xx. Let yy be the last vertex of PuP_u that was popped before uu (there is one, xx), and zz its successor on PuP_u. When yy was popped it was complete, and relaxing (y,z)(y, z) set d^(z)=d(z)<B\hat d(z) = d(z) < B and pushed zz with key d(z)d(z), since zz was not yet popped. That entry stays valid, and d(z)≤d(u)<d^(u)d(z) \le d(u) < \hat d(u), so zz is popped before uu unless z=uz = u; in both cases we get a contradiction. The keys popped are increasing by (K3), and xx is popped first.

The result. If at most kk vertices are popped, the heap ran empty, B′=BB' = B, and UU is the set of popped vertices. Otherwise B′B' is the key of the last popped vertex, and UU is the popped vertices with smaller keys. In both cases every v∈U~(B′)v \in \tilde U(B') is popped: if not, take the last popped vertex yy on PvP_v and its successor zz; as above zz was pushed with key d(z)≤d(v)<B′d(z) \le d(v) < B', so it would have been popped before the last pop. Every popped vertex is complete and lies in U~(B)\tilde U(B) by Fact B. So (R1) and (R2) hold. For (R3), B′=B>d^(x)B' = B > \hat d(x) by (P1), or B′B' is the key of a vertex popped after xx, so B′>d^(x)B' > \hat d(x) by (K3).

Lemma 2 (FindPivots). In a proper call, after line L3, every v∈U~(B)v \in \tilde U(B) is in WW and complete, or has a complete vertex of PP on PvP_v. Also P⊆SP \subseteq S and W⊆U~(B)W \subseteq \tilde U(B).

Proof. W⊆U~(B)W \subseteq \tilde U(B): S⊆U~(B)S \subseteq \tilde U(B) by (P1), and every other vertex enters WW through a relaxation from a vertex of WW with a candidate below BB, so Fact B applies.

Let v∈U~(B)v \in \tilde U(B). By Fact A, PvP_v has a vertex of SS that is complete at the start; let y0y_0 be the last one, and y0,z1,…,zj=vy_0, z_1, \ldots, z_j = v the rest of PvP_v. By induction on ii, after round i≤min⁡(j,k)i \le \min(j, k) the vertex ziz_i is complete and in the next layer: round ii relaxes from zi−1z_{i-1} (y0y_0 is in the first layer, all of SS), the candidate is d(zi)≤d^(zi)d(z_i) \le \hat d(z_i), and it is below BB.

If FindPivots returns early with P=SP = S, then y0∈Py_0 \in P is complete and on PvP_v. If j≤kj \le k, then vv is complete and in WW. Otherwise z1,…,zkz_1, \ldots, z_k are complete and in WW.

In the last case, consider the forest FF on WW: (p,q)(p, q) is an edge when pp is the stored predecessor of qq, p∈Wp \in W, and d^(q)=d^(p)+w(p,q)\hat d(q) = \hat d(p) + w(p, q). Each vertex has at most one parent, and keys increase along edges by (K3), so FF is a forest. The edges (y0,z1),(z1,z2),…,(zk−1,zk)(y_0, z_1), (z_1, z_2), \ldots, (z_{k-1}, z_k) are in FF, since these vertices are complete and Pzi=Pzi−1+ziP_{z_i} = P_{z_{i-1}} + z_i.

If qq is complete and (p,q)∈F(p, q) \in F, then d^(p)+w=d(q)≤d(p)+w\hat d(p) + w = d(q) \le d(p) + w, so d^(p)=d(p)\hat d(p) = d(p) by (K2), and Pq=Pp+qP_q = P_p + q. So every ancestor in FF of a complete vertex is complete and lies on its path. Let rr be the root of the tree of y0y_0. Then rr is complete and on Py0P_{y_0}, a prefix of PvP_v.

We claim r∈Sr \in S. If not, rr entered WW when a relaxation during FindPivots set its label. At the last such time, its label became d^(p)+w\hat d(p) + w with p∈Wp \in W the new predecessor. If d^(p)\hat d(p) later dropped, then d^(p)+w<d^(r)=d(r)\hat d(p) + w < \hat d(r) = d(r), which is impossible. So (p,r)∈F(p, r) \in F and rr is not a root.

So rr is a root in SS whose tree has at least k+1k + 1 vertices, hence r∈Pr \in P. □\square

Lemma 3 (the loop). Consider a proper call at level l≥1l \ge 1, and let done be the union of the sets UiU_i returned so far. Call v∈U~(B)∖v \in \tilde U(B) \setminus done pending. Before every iteration, and when the loop stops:

  • (J0) every vertex in done is complete and in U~(B)\tilde U(B); every value in DD is below BB, and it is at least Bi′B'_i of the last subcall, if there was one;
  • (J) every pending vv satisfies one of the following:
    • (a) v∈Wv \in W and vv is complete;
    • (b) PvP_v contains a vertex of done; for the last such vertex, its successor zz on PvP_v is complete and is a key of DD with value d(z)d(z);
    • (c) PvP_v contains no vertex of done, and some complete vertex xx on PvP_v is a key of DD with value d(x)d(x).

Also, every key of DD lies in U~(B)\tilde U(B).

Start. done is empty, and DD holds the pivots with their labels. By Lemma 2 each pending vv satisfies (a) or (c). Pivots are in S⊆U~(B)S \subseteq \tilde U(B).

Values. DD keeps the smaller value when a key is inserted again. A key zz with value d(z)d(z) keeps that value until it is pulled, since no candidate is below d(z)d(z).

The subcall is proper. Line L6 pulls the keys with values below BiB_i; values are keys of different vertices, so they are distinct. If DD is left empty, Bi=BB_i = B. The vertices of done are then removed from SiS_i.

  • (P1) holds because d^(x)\hat d(x) is at most the value of xx in DD, which is below BiB_i.
  • For (P2), let vv be incomplete with d(v)<Bi≤Bd(v) < B_i \le B. By (P2) of this call and Fact A, v∈U~(B)v \in \tilde U(B). It is not in done, because those vertices are complete, and it is not in case (a). In case (b) or (c), the vertex zz or xx has value at most d(v)<Bid(v) < B_i, so it was pulled. It is not in done, so it is in SiS_i, and it is complete and on PvP_v.

If SiS_i ends up empty, no subcall happens, and the iteration only removes keys of done vertices; such keys are never the zz or xx of (J). So (J0) and (J) still hold.

After the subcall. By induction, the subcall returns with (R1) to (R3) for (Bi,Si)(B_i, S_i). Every vertex of SiS_i is a key of DD, so it lies in U~(B)\tilde U(B). Hence every vertex of UiU_i is complete, and its path visits SiS_i and so SS, which keeps done inside U~(B)\tilde U(B). Relaxations from UiU_i in line L8 insert or prepend only candidates cc with Bi′≤c<BB'_i \le c < B; by Fact B these vertices lie in U~(B)\tilde U(B). All other keys have values at least Bi≥Bi′B_i \ge B'_i. So (J0) holds.

(J) after the iteration. Let vv be pending afterwards. Case (a) persists, so assume vv is not in case (a).

  • If PvP_v meets done, let yy be the last vertex of PvP_v in done and zz its successor; zz is complete, because Pz=Py+zP_z = P_y + z and the edge was relaxed after yy became complete.
    • If y∈Uiy \in U_i, line L8 relaxed (y,z)(y, z) and the candidate is d(z)<Bd(z) < B. If d(z)<Bi′d(z) < B'_i, then z∈U~i(Bi′)⊆Uiz \in \tilde U_i(B'_i) \subseteq U_i (as PzP_z visits SiS_i), which contradicts the choice of yy. So d(z)≥Bi′d(z) \ge B'_i, and line L9 or line L10 put zz into DD with value d(z)d(z).
    • If yy was in done before, then (J) before the iteration gave zz as a key with value d(z)d(z). If zz was pulled now, it is in SiS_i and not in UiU_i. Then d(z)≥Bi′d(z) \ge B'_i, because otherwise z∈U~i(Bi′)⊆Uiz \in \tilde U_i(B'_i) \subseteq U_i. So line L10 prepends it again with value d^(z)=d(z)<Bi\hat d(z) = d(z) < B_i.
  • If PvP_v does not meet done, then before the iteration it was in case (c) with some xx. If xx was pulled, x∉Uix \notin U_i, so as above d(x)≥Bi′d(x) \ge B'_i, and line L10 puts it back with value d(x)d(x).

Termination of the loop. Take a subcall. Let y∈Siy \in S_i have the smallest label at the start of the subcall. By (R3), Bi′>d^(y)≥d(y)B'_i > \hat d(y) \ge d(y), so y∈U~i(Bi′)⊆Uiy \in \tilde U_i(B'_i) \subseteq U_i. Since yy is not in done, done grows by at least one vertex. So there are at most nn subcalls, and between two of them each iteration removes keys without adding any. Every iteration pulls at least one key, so the loop stops. □\square

Lemma 4 (the result of the call). A proper call at level l≥1l \ge 1 satisfies (R1) to (R3).

(R1) holds by line L11. For (R2), let v∈U~(B′)v \in \tilde U(B'), and suppose vv is pending. Cases (b) and (c) give a key with value at most d(v)<B′d(v) < B'. But when the loop stops, either DD is empty, or every value is at least Bi′B'_i of the last subcall, which is at least B′B', by (J0). So vv is in case (a): v∈Wv \in W, complete, with d^(v)<B′\hat d(v) < B', and line L11 adds it to UU. Every vertex added to UU is in done, which is complete and in U~(B)\tilde U(B) by (J0), or is a vertex of WW with d^<B′\hat d < B'. Such a vertex lies in U~(B′)\tilde U(B') by Lemma 2, so it is in done or in case (a), and in both cases it is complete.

For (R3): if there was no subcall, B′=B>d^(x)B' = B > \hat d(x) for every x∈Sx \in S by (P1). Otherwise B′=min⁡(Bi′,B)B' = \min(B'_i, B) for the last subcall. BB is larger than every label in SS. By (R3) of the subcall, Bi′>d^(y)≥d(y)B'_i > \hat d(y) \ge d(y) for some y∈Siy \in S_i. By Fact A, PyP_y has a vertex x∈Sx \in S that is complete at the start, and d(y)≥d(x)=d^(x)d(y) \ge d(x) = \hat d(x), because dd does not decrease along PyP_y. ■\blacksquare (lemma)

The top call. With S={s}S = \{s\}, B=∞B = \infty and d^(s)=0\hat d(s) = 0 the call is proper: ss is complete, and every reachable vv has ss on PvP_v. Its level L=⌈log⁡2(n+1)/t⌉L = \lceil \log_2(n+1)/t \rceil gives k⋅2Lt≥n+1>∣done∣k \cdot 2^{Lt} \ge n + 1 > |\text{done}|, so the loop stops only when DD is empty. Then (J) leaves only case (a): every reachable vertex is in done or in WW, and complete. So d^(v)=d(v)\hat d(v) = d(v), whose first entry is δ(s,v)\delta(s, v). A vertex that ss does not reach is never the target of a relaxation from a finite label, so it keeps d^=∞\hat d = \infty. The recursion depth is LL, every FindPivots is kk rounds, every base case pops each vertex at most once, and every loop stops by Lemma 3, so the algorithm terminates.

The stored keys. The implementation stores only the first four entries of a key: (length, edge count, vv, predecessor). Labels of different vertices differ in the third entry. Two candidates for the same vertex with the same four entries come from the same predecessor uu, with the same length and edge count for uu. For these, the code compares with ≤\le and keeps the newer one. Its full key is never larger, because the label of uu only decreases. So every comparison of stored labels agrees with the comparison of full keys, except between two such candidates; they have equal lengths, so the distances are not affected. ■\blacksquare

Back to Breaking the sorting barrier for directed SSSP