##// END OF EJS Templates
stabletailgraph: extract _parents util func
pacien -
r51420:dc372251 default
parent child Browse files
Show More
@@ -1,116 +1,120
1 1 # stabletailsort.py - stable ordering of revisions
2 2 #
3 3 # Copyright 2021-2023 Pacien TRAN-GIRARD <pacien.trangirard@pacien.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """
9 9 Stable-tail sort computation.
10 10
11 11 The "stable-tail sort", or STS, is a reverse topological ordering of the
12 12 ancestors of a node, which tends to share large suffixes with the stable-tail
13 13 sort of ancestors and other nodes, giving it its name.
14 14
15 15 Its properties should make it suitable for making chunks of ancestors with high
16 16 reuse and incrementality for example.
17 17
18 18 This module and implementation are experimental. Most functions are not yet
19 19 optimised to operate on large production graphs.
20 20 """
21 21
22 22 import itertools
23 23 from ..node import nullrev
24 24 from .. import ancestor
25 25
26 26
27 27 def _sorted_parents(cl, p1, p2):
28 28 """
29 29 Chooses and returns the pair (px, pt) from (p1, p2).
30 30
31 31 Where
32 32 "px" denotes the parent starting the "exclusive" part, and
33 33 "pt" denotes the parent starting the "Tail" part.
34 34
35 35 "px" is chosen as the parent with the lowest rank with the goal of
36 36 minimising the size of the exclusive part and maximise the size of the
37 37 tail part, hopefully reducing the overall complexity of the stable-tail
38 38 sort.
39 39
40 40 In case of equal ranks, the stable node ID is used as a tie-breaker.
41 41 """
42 42 r1, r2 = cl.fast_rank(p1), cl.fast_rank(p2)
43 43 if r1 < r2:
44 44 return (p1, p2)
45 45 elif r1 > r2:
46 46 return (p2, p1)
47 47 elif cl.node(p1) < cl.node(p2):
48 48 return (p1, p2)
49 49 else:
50 50 return (p2, p1)
51 51
52 52
53 53 def _nonoedipal_parent_revs(cl, rev):
54 54 """
55 55 Returns the non-Ε“dipal parent pair of the given revision.
56 56
57 57 An Ε“dipal merge is a merge with parents p1, p2 with either
58 58 p1 in ancestors(p2) or p2 in ancestors(p1).
59 59 In the first case, p1 is the Ε“dipal parent.
60 60 In the second case, p2 is the Ε“dipal parent.
61 61
62 62 Ε’dipal edges start empty exclusive parts. They do not bring new ancestors.
63 63 As such, they can be skipped when computing any topological sort or any
64 64 iteration over the ancestors of a node.
65 65
66 66 The Ε“dipal edges are eliminated here using the rank information.
67 67 """
68 68 p1, p2 = cl.parentrevs(rev)
69 69 if p1 == nullrev or cl.fast_rank(p2) == cl.fast_rank(rev) - 1:
70 70 return p2, nullrev
71 71 elif p2 == nullrev or cl.fast_rank(p1) == cl.fast_rank(rev) - 1:
72 72 return p1, nullrev
73 73 else:
74 74 return p1, p2
75 75
76 76
77 def _parents(cl, rev):
78 p1, p2 = _nonoedipal_parent_revs(cl, rev)
79 if p2 == nullrev:
80 return p1, p2
81
82 return _sorted_parents(cl, p1, p2)
83
84
77 85 def _stable_tail_sort_naive(cl, head_rev):
78 86 """
79 87 Naive topological iterator of the ancestors given by the stable-tail sort.
80 88
81 89 The stable-tail sort of a node "h" is defined as the sequence:
82 90 sts(h) := [h] + excl(h) + sts(pt(h))
83 91 where excl(h) := u for u in sts(px(h)) if u not in ancestors(pt(h))
84 92
85 93 This implementation uses a call-stack whose size is
86 94 O(number of open merges).
87 95
88 96 As such, this implementation exists mainly as a defining reference.
89 97 """
90 98 cursor_rev = head_rev
91 99 while cursor_rev != nullrev:
92 100 yield cursor_rev
93 101
94 p1, p2 = _nonoedipal_parent_revs(cl, cursor_rev)
95 if p1 == nullrev:
96 cursor_rev = p2
97 elif p2 == nullrev:
98 cursor_rev = p1
102 px, pt = _parents(cl, cursor_rev)
103 if pt == nullrev:
104 cursor_rev = px
99 105 else:
100 px, pt = _sorted_parents(cl, p1, p2)
101
102 106 tail_ancestors = ancestor.lazyancestors(
103 107 cl.parentrevs, (pt,), inclusive=True
104 108 )
105 109 exclusive_ancestors = (
106 110 a
107 111 for a in _stable_tail_sort_naive(cl, px)
108 112 if a not in tail_ancestors
109 113 )
110 114
111 115 # Notice that excl(cur) is disjoint from ancestors(pt),
112 116 # so there is no double-counting:
113 117 # rank(cur) = len([cur]) + len(excl(cur)) + rank(pt)
114 118 excl_part_size = cl.fast_rank(cursor_rev) - cl.fast_rank(pt) - 1
115 119 yield from itertools.islice(exclusive_ancestors, excl_part_size)
116 120 cursor_rev = pt
General Comments 0
You need to be logged in to leave comments. Login now