Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/networkx/utils/heaps.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:32:28 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:d30785e31577 | 1:56ad4e20f292 |
---|---|
1 """ | |
2 Min-heaps. | |
3 """ | |
4 | |
5 __author__ = """ysitu <ysitu@users.noreply.github.com>""" | |
6 # Copyright (C) 2014 ysitu <ysitu@users.noreply.github.com> | |
7 # All rights reserved. | |
8 # BSD license. | |
9 | |
10 from heapq import heappop, heappush | |
11 from itertools import count | |
12 import networkx as nx | |
13 | |
14 __all__ = ['MinHeap', 'PairingHeap', 'BinaryHeap'] | |
15 | |
16 | |
17 class MinHeap(object): | |
18 """Base class for min-heaps. | |
19 | |
20 A MinHeap stores a collection of key-value pairs ordered by their values. | |
21 It supports querying the minimum pair, inserting a new pair, decreasing the | |
22 value in an existing pair and deleting the minimum pair. | |
23 """ | |
24 | |
25 class _Item(object): | |
26 """Used by subclassess to represent a key-value pair. | |
27 """ | |
28 __slots__ = ('key', 'value') | |
29 | |
30 def __init__(self, key, value): | |
31 self.key = key | |
32 self.value = value | |
33 | |
34 def __repr__(self): | |
35 return repr((self.key, self.value)) | |
36 | |
37 def __init__(self): | |
38 """Initialize a new min-heap. | |
39 """ | |
40 self._dict = {} | |
41 | |
42 def min(self): | |
43 """Query the minimum key-value pair. | |
44 | |
45 Returns | |
46 ------- | |
47 key, value : tuple | |
48 The key-value pair with the minimum value in the heap. | |
49 | |
50 Raises | |
51 ------ | |
52 NetworkXError | |
53 If the heap is empty. | |
54 """ | |
55 raise NotImplementedError | |
56 | |
57 def pop(self): | |
58 """Delete the minimum pair in the heap. | |
59 | |
60 Returns | |
61 ------- | |
62 key, value : tuple | |
63 The key-value pair with the minimum value in the heap. | |
64 | |
65 Raises | |
66 ------ | |
67 NetworkXError | |
68 If the heap is empty. | |
69 """ | |
70 raise NotImplementedError | |
71 | |
72 def get(self, key, default=None): | |
73 """Returns the value associated with a key. | |
74 | |
75 Parameters | |
76 ---------- | |
77 key : hashable object | |
78 The key to be looked up. | |
79 | |
80 default : object | |
81 Default value to return if the key is not present in the heap. | |
82 Default value: None. | |
83 | |
84 Returns | |
85 ------- | |
86 value : object. | |
87 The value associated with the key. | |
88 """ | |
89 raise NotImplementedError | |
90 | |
91 def insert(self, key, value, allow_increase=False): | |
92 """Insert a new key-value pair or modify the value in an existing | |
93 pair. | |
94 | |
95 Parameters | |
96 ---------- | |
97 key : hashable object | |
98 The key. | |
99 | |
100 value : object comparable with existing values. | |
101 The value. | |
102 | |
103 allow_increase : bool | |
104 Whether the value is allowed to increase. If False, attempts to | |
105 increase an existing value have no effect. Default value: False. | |
106 | |
107 Returns | |
108 ------- | |
109 decreased : bool | |
110 True if a pair is inserted or the existing value is decreased. | |
111 """ | |
112 raise NotImplementedError | |
113 | |
114 def __nonzero__(self): | |
115 """Returns whether the heap if empty. | |
116 """ | |
117 return bool(self._dict) | |
118 | |
119 def __bool__(self): | |
120 """Returns whether the heap if empty. | |
121 """ | |
122 return bool(self._dict) | |
123 | |
124 def __len__(self): | |
125 """Returns the number of key-value pairs in the heap. | |
126 """ | |
127 return len(self._dict) | |
128 | |
129 def __contains__(self, key): | |
130 """Returns whether a key exists in the heap. | |
131 | |
132 Parameters | |
133 ---------- | |
134 key : any hashable object. | |
135 The key to be looked up. | |
136 """ | |
137 return key in self._dict | |
138 | |
139 | |
140 def _inherit_doc(cls): | |
141 """Decorator for inheriting docstrings from base classes. | |
142 """ | |
143 def func(fn): | |
144 fn.__doc__ = cls.__dict__[fn.__name__].__doc__ | |
145 return fn | |
146 return func | |
147 | |
148 | |
149 class PairingHeap(MinHeap): | |
150 """A pairing heap. | |
151 """ | |
152 | |
153 class _Node(MinHeap._Item): | |
154 """A node in a pairing heap. | |
155 | |
156 A tree in a pairing heap is stored using the left-child, right-sibling | |
157 representation. | |
158 """ | |
159 __slots__ = ('left', 'next', 'prev', 'parent') | |
160 | |
161 def __init__(self, key, value): | |
162 super(PairingHeap._Node, self).__init__(key, value) | |
163 # The leftmost child. | |
164 self.left = None | |
165 # The next sibling. | |
166 self.next = None | |
167 # The previous sibling. | |
168 self.prev = None | |
169 # The parent. | |
170 self.parent = None | |
171 | |
172 def __init__(self): | |
173 """Initialize a pairing heap. | |
174 """ | |
175 super(PairingHeap, self).__init__() | |
176 self._root = None | |
177 | |
178 @_inherit_doc(MinHeap) | |
179 def min(self): | |
180 if self._root is None: | |
181 raise nx.NetworkXError('heap is empty.') | |
182 return (self._root.key, self._root.value) | |
183 | |
184 @_inherit_doc(MinHeap) | |
185 def pop(self): | |
186 if self._root is None: | |
187 raise nx.NetworkXError('heap is empty.') | |
188 min_node = self._root | |
189 self._root = self._merge_children(self._root) | |
190 del self._dict[min_node.key] | |
191 return (min_node.key, min_node.value) | |
192 | |
193 @_inherit_doc(MinHeap) | |
194 def get(self, key, default=None): | |
195 node = self._dict.get(key) | |
196 return node.value if node is not None else default | |
197 | |
198 @_inherit_doc(MinHeap) | |
199 def insert(self, key, value, allow_increase=False): | |
200 node = self._dict.get(key) | |
201 root = self._root | |
202 if node is not None: | |
203 if value < node.value: | |
204 node.value = value | |
205 if node is not root and value < node.parent.value: | |
206 self._cut(node) | |
207 self._root = self._link(root, node) | |
208 return True | |
209 elif allow_increase and value > node.value: | |
210 node.value = value | |
211 child = self._merge_children(node) | |
212 # Nonstandard step: Link the merged subtree with the root. See | |
213 # below for the standard step. | |
214 if child is not None: | |
215 self._root = self._link(self._root, child) | |
216 # Standard step: Perform a decrease followed by a pop as if the | |
217 # value were the smallest in the heap. Then insert the new | |
218 # value into the heap. | |
219 # if node is not root: | |
220 # self._cut(node) | |
221 # if child is not None: | |
222 # root = self._link(root, child) | |
223 # self._root = self._link(root, node) | |
224 # else: | |
225 # self._root = (self._link(node, child) | |
226 # if child is not None else node) | |
227 return False | |
228 else: | |
229 # Insert a new key. | |
230 node = self._Node(key, value) | |
231 self._dict[key] = node | |
232 self._root = self._link(root, node) if root is not None else node | |
233 return True | |
234 | |
235 def _link(self, root, other): | |
236 """Link two nodes, making the one with the smaller value the parent of | |
237 the other. | |
238 """ | |
239 if other.value < root.value: | |
240 root, other = other, root | |
241 next = root.left | |
242 other.next = next | |
243 if next is not None: | |
244 next.prev = other | |
245 other.prev = None | |
246 root.left = other | |
247 other.parent = root | |
248 return root | |
249 | |
250 def _merge_children(self, root): | |
251 """Merge the subtrees of the root using the standard two-pass method. | |
252 The resulting subtree is detached from the root. | |
253 """ | |
254 node = root.left | |
255 root.left = None | |
256 if node is not None: | |
257 link = self._link | |
258 # Pass 1: Merge pairs of consecutive subtrees from left to right. | |
259 # At the end of the pass, only the prev pointers of the resulting | |
260 # subtrees have meaningful values. The other pointers will be fixed | |
261 # in pass 2. | |
262 prev = None | |
263 while True: | |
264 next = node.next | |
265 if next is None: | |
266 node.prev = prev | |
267 break | |
268 next_next = next.next | |
269 node = link(node, next) | |
270 node.prev = prev | |
271 prev = node | |
272 if next_next is None: | |
273 break | |
274 node = next_next | |
275 # Pass 2: Successively merge the subtrees produced by pass 1 from | |
276 # right to left with the rightmost one. | |
277 prev = node.prev | |
278 while prev is not None: | |
279 prev_prev = prev.prev | |
280 node = link(prev, node) | |
281 prev = prev_prev | |
282 # Now node can become the new root. Its has no parent nor siblings. | |
283 node.prev = None | |
284 node.next = None | |
285 node.parent = None | |
286 return node | |
287 | |
288 def _cut(self, node): | |
289 """Cut a node from its parent. | |
290 """ | |
291 prev = node.prev | |
292 next = node.next | |
293 if prev is not None: | |
294 prev.next = next | |
295 else: | |
296 node.parent.left = next | |
297 node.prev = None | |
298 if next is not None: | |
299 next.prev = prev | |
300 node.next = None | |
301 node.parent = None | |
302 | |
303 | |
304 class BinaryHeap(MinHeap): | |
305 """A binary heap. | |
306 """ | |
307 | |
308 def __init__(self): | |
309 """Initialize a binary heap. | |
310 """ | |
311 super(BinaryHeap, self).__init__() | |
312 self._heap = [] | |
313 self._count = count() | |
314 | |
315 @_inherit_doc(MinHeap) | |
316 def min(self): | |
317 dict = self._dict | |
318 if not dict: | |
319 raise nx.NetworkXError('heap is empty') | |
320 heap = self._heap | |
321 pop = heappop | |
322 # Repeatedly remove stale key-value pairs until a up-to-date one is | |
323 # met. | |
324 while True: | |
325 value, _, key = heap[0] | |
326 if key in dict and value == dict[key]: | |
327 break | |
328 pop(heap) | |
329 return (key, value) | |
330 | |
331 @_inherit_doc(MinHeap) | |
332 def pop(self): | |
333 dict = self._dict | |
334 if not dict: | |
335 raise nx.NetworkXError('heap is empty') | |
336 heap = self._heap | |
337 pop = heappop | |
338 # Repeatedly remove stale key-value pairs until a up-to-date one is | |
339 # met. | |
340 while True: | |
341 value, _, key = heap[0] | |
342 pop(heap) | |
343 if key in dict and value == dict[key]: | |
344 break | |
345 del dict[key] | |
346 return (key, value) | |
347 | |
348 @_inherit_doc(MinHeap) | |
349 def get(self, key, default=None): | |
350 return self._dict.get(key, default) | |
351 | |
352 @_inherit_doc(MinHeap) | |
353 def insert(self, key, value, allow_increase=False): | |
354 dict = self._dict | |
355 if key in dict: | |
356 old_value = dict[key] | |
357 if value < old_value or (allow_increase and value > old_value): | |
358 # Since there is no way to efficiently obtain the location of a | |
359 # key-value pair in the heap, insert a new pair even if ones | |
360 # with the same key may already be present. Deem the old ones | |
361 # as stale and skip them when the minimum pair is queried. | |
362 dict[key] = value | |
363 heappush(self._heap, (value, next(self._count), key)) | |
364 return value < old_value | |
365 return False | |
366 else: | |
367 dict[key] = value | |
368 heappush(self._heap, (value, next(self._count), key)) | |
369 return True |