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