1 '''
2 This module offert classes to manipulate graphs
3 '''
4
5 import sys
6
7
8 from obitools.utils import progressBar
9
10
12 '''
13 Allow to manage convertion between an arbitrarly hashable python
14 value and an unique integer key
15 '''
16
18 '''
19 Constructor
20 '''
21 self.__max=0
22 self.__reverse=[]
23
25 '''
26 Return the python value associated to an integer index.
27
28 @param index: an index value
29 @type index: integer
30
31 @raise IndexError: if the index is not used in this
32 Indexer instance
33 '''
34 return self.__reverse[index]
35
37 if dict.__contains__(self,key):
38 return dict.__getitem__(self,key)
39 elif strict:
40 raise KeyError,key
41 else:
42 value = self.__max
43 self[key]= value
44 self.__reverse.append(key)
45 self.__max+=1
46 return value
47
49 '''
50 Return the integer key associated to a python value.
51
52 @param key: the value to index
53 @type key: an hashable python value
54 @return: an unique integer value associated to the key
55 @rtype: int
56
57 '''
58 return self.getIndex(key)
59
61 '''
62 Implement equa operator. Two Indexer instances are
63 equals if they are physically the same instance
64 @param index: the seconde Indexer
65 @type index: an Indexer instance
66
67 @return: True is the two instances are the same
68 @rtype: bool
69 '''
70 return id(self)==id(index)
71
72
74 '''
75 Class used to represent directed or undirected graph.
76
77 Only one edge can connect two nodes
78 '''
79 - def __init__(self,label='G',directed=False,indexer=None,nodes=None,edges=None):
80 '''
81 Graph constructor.
82
83 @param label: Graph name, set to 'G' by default
84 @type label: str
85 @param directed: true for directed graph, set to False by defalt
86 @type directed: boolean
87 @param indexer: node label indexer
88 @type indexer: Indexer instance
89 @param nodes: set of nodes to add to the graph
90 @type nodes: iterable value
91 @param edges: set of edges to add to the graph
92 @type edges: iterable value
93 '''
94 self._directed=directed
95 if indexer is None:
96 indexer = Indexer()
97 self._index = indexer
98 self._node = {}
99 self._node_attrs = {}
100 self._edge_attrs = {}
101 self._label=label
102
104 n = Graph(self._label+"_compact",self._directed,self._index)
105 return n
106
107 - def addNode(self,node=None,index=None,**data):
108 if index is None:
109 index = self._index[node]
110
111 if index not in self._node:
112 self._node[index]=set()
113
114 if data:
115 if index in self._node_attrs:
116 self._node_attrs[index].update(data)
117 else:
118 self._node_attrs[index]=dict(data)
119 return index
120
121 - def getNode(self,node=None,index=None):
122 if index is None:
123 index = self._index.getIndex(node, True)
124 return Node(index,self)
125
127 bestScore=0
128 best=None
129 for n in self:
130 score = estimator(n)
131 if best is None or score > bestScore:
132 bestScore = score
133 best=n
134 return best
135
136
137 - def delNode(self,node=None,index=None):
138 if index is None:
139 index = self._index[node]
140 for n in self._node:
141 if n!=index:
142 e = self._node[n]
143 if index in e:
144 if (n,index) in self._edge_attrs:
145 del self._edge_attrs[(n,index)]
146 e.remove(index)
147 e = self._node[index]
148 for n in e:
149 if (index,n) in self._edge_attrs:
150 del self._edge_attrs[(index,n)]
151 del self._node[index]
152 if index in self._node_attrs:
153 del self._node_attrs[index]
154
155
156 - def addEdge(self,node1=None,node2=None,index1=None,index2=None,**data):
157 index1=self.addNode(node1, index1)
158 index2=self.addNode(node2, index2)
159
160 self._node[index1].add(index2)
161
162 if not self._directed:
163 self._node[index2].add(index1)
164
165 if data:
166 if (index1,index2) not in self._edge_attrs:
167 data =dict(data)
168 self._edge_attrs[(index1,index2)]=data
169 if not self._directed:
170 self._edge_attrs[(index2,index1)]=data
171 else:
172 self._edge_attrs.update(data)
173
174 return (index1,index2)
175
176 - def getEdge(self,node1=None,node2=None,index1=None,index2=None):
177 '''
178
179 @param node1:
180 @type node1:
181 @param node2:
182 @type node2:
183 @param index1:
184 @type index1:
185 @param index2:
186 @type index2:
187 '''
188 node1=self.getNode(node1, index1)
189 node2=self.getNode(node2, index2)
190 return Edge(node1,node2)
191
192 - def delEdge(self,node1=None,node2=None,index1=None,index2=None):
193 if index1 is None:
194 index1 = self._index[node1]
195 if index2 is None:
196 index2 = self._index[node2]
197 if index1 in self._node and index2 in self._node[index]:
198 self._node[index1].remove(index2)
199 if (index1,index2) in self._node_attrs:
200 del self._node_attrs[(index1,index2)]
201 if not self._directed:
202 self._node[index2].remove(index1)
203 if (index2,index1) in self._node_attrs:
204 del self._node_attrs[(index2,index1)]
205
207 for n1 in self._node:
208 for n2 in self._node[n1]:
209 if self._directed or n1 <= n2:
210 e = self.getEdge(index1=n1, index2=n2)
211 if predicat is None or predicat(e):
212 yield e
213
214
216 for n in self._node:
217 node = self.getNode(index=n)
218 if predicat is None or predicat(node):
219 yield node
220
226
228 if index is None:
229 index=self.getNode(node).index
230 return self._node[index]
231
233 n = reduce(lambda x,y:x+y, (len(z) for z in self._node.itervalues()),0)
234 if not self._directed:
235 n=n/2
236 return n
237
239 sub = Graph(name,self._directed,self._index)
240 if not isinstance(nodes, set):
241 nodes = set(nodes)
242 for n in nodes:
243 sub._node[n]=nodes & self._node[n]
244 if n in self._node_attrs:
245 sub._node_attrs[n]=dict(self._node_attrs)
246 for n2 in sub._node[n]:
247 if not self._directed:
248 if n < n2:
249 if (n,n2) in self._edge_attrs:
250 data=dict(self._edge_attrs[(n,n2)])
251 sub._edge_attrs[(n,n2)]=data
252 sub._edge_attrs[(n2,n)]=data
253 else:
254 if (n,n2) in self._edge_attrs:
255 data=dict(self._edge_attrs[(n,n2)])
256 sub._edge_attrs[(n,n2)]=data
257 return sub
258
260 return len(self._node)
261
264
267
270
272 if self._directed:
273 kw ='digraph'
274 else:
275 kw='graph'
276
277 nodes = "\n ".join([str(x) for x in self])
278 edges = "\n ".join([str(x) for x in self.edgeIterator()])
279
280 return "%s %s {\n %s\n\n %s\n}" % (kw,self._label,nodes,edges)
281
284 '''
285 Constructor on Node instance.
286
287 Normaly this constructor is only used by Graph methods.
288
289 @param index: Index of the node in the graph
290 @type index: int
291 @param graph: graph instance owning the node
292 @type graph: obitools.graph.Graph
293 '''
294 self.index = index
295 self.__graph = graph
296
298 '''
299 return graph owning this node.
300
301 @rtype: L{obitools.graph.Graph}
302 '''
303 return self.__graph
304
305
307 '''
308 return label associated to this node.
309 '''
310 return self.__graph._index.getLabel(self.index)
311
312
314 '''
315 test is the node instance has a property named 'key'.
316
317 @param key: the name of a property
318 @type key: str
319
320 @return: True if the nade has a property named <key>
321 @rtype: bool
322 '''
323 if self.index in self.__graph._node_attrs:
324 return key in self.__graph._node_attrs[self.index]
325 else:
326 return False
327
329 '''
330 iterate through the nodes directly connected to
331 this node.
332
333 @param nodePredicat: a function accepting one node as parameter
334 and returning True if this node must be
335 returned by the iterator.
336 @type nodePredicat: function
337
338 @param edgePredicat: a function accepting one edge as parameter
339 and returning True if the edge linking self and
340 the current must be considered.
341 @type edgePredicat: function
342
343
344 @rtype: iterator on Node instances
345 '''
346 for n in self.neighbourIndexIterator(nodePredicat, edgePredicat):
347 node = self.graph.getNode(index=n)
348 yield node
349
351 '''
352 Return a set of node indexes directely connected
353 to this node.
354
355 @attention: do not change this set unless you know
356 exactly what you do.
357
358 @rtype: set of int
359 '''
360 return self.__graph._node[self.index]
361
363 '''
364 iterate through the node indexes directly connected to
365 this node.
366
367 @param nodePredicat: a function accepting one node as parameter
368 and returning True if this node must be
369 returned by the iterator.
370 @type nodePredicat: function
371
372 @param edgePredicat: a function accepting one edge as parameter
373 and returning True if the edge linking self and
374 the current must be considered.
375 @type edgePredicat: function
376
377 @rtype: iterator on int
378 '''
379 for n in self.neighbourIndexSet():
380 if nodePredicat is None or nodePredicat(self.__graph.getNode(index=n)):
381 if edgePredicat is None or edgePredicat(self.__graph.getEdge(index1=self.index,index2=n)):
382 yield n
383
384 - def degree(self,nodeIndexes=None):
385 '''
386 return count of edges linking this node to the
387 set of nodes describes by their index in nodeIndexes
388
389 @param nodeIndexes: set of node indexes.
390 if set to None, all nodes of the
391 graph are take into account.
392 Set to None by default.
393 @type nodeIndexes: set of int
394
395 @rtype: int
396 '''
397 if nodeIndexes is None:
398 return len(self.__graph._node[self.index])
399 else:
400 return len(self.__graph._node[self.index] & nodeIndexes)
401
403 '''
404 Return the set of node index in the same connected component.
405
406 @param nodePredicat: a function accepting one node as parameter
407 and returning True if this node must be
408 returned by the iterator.
409 @type nodePredicat: function
410
411 @param edgePredicat: a function accepting one edge as parameter
412 and returning True if the edge linking self and
413 the current must be considered.
414 @type edgePredicat: function
415
416
417 @rtype: set of int
418 '''
419 cc=set([self.index])
420 added = set(x for x in self.neighbourIndexIterator(nodePredicat, edgePredicat))
421 while added:
422 cc |= added
423 added = reduce(lambda x,y : x | y,
424 (set(z for z in self.graph.getNode(index=c).neighbourIndexIterator(nodePredicat, edgePredicat))
425 for c in added),
426 set())
427 added -= cc
428 return cc
429
431 '''
432 Iterate through the nodes in the same connected
433 component.
434
435 @rtype: iterator on L{Node} instance
436 '''
437 for c in self.componentIndexSet(nodePredicat, edgePredicat):
438 yield self.graph.getNode(c)
439
441 '''
442 Iterate through the shortest path sourcing
443 from this node. if nodes is not None, iterates
444 only path linkink this node to one node listed in
445 nodes
446
447 @param nodes: set of node index
448 @type nodes: iterable on int
449
450 @return: an iterator on list of int describing path
451 @rtype: iterator on list of int
452 '''
453 if nodes is not None:
454 nodes = set(nodes)
455
456
457 Q=[(self.index,-1)]
458
459 gray = set([self.index])
460 paths = {}
461
462 while Q and (nodes is None or nodes):
463 u,p = Q.pop()
464 paths[u]=p
465 next = self.graph._node[u] - gray
466 gray|=next
467 Q.extend((x,u) for x in next)
468 if nodes is None or u in nodes:
469 if nodes:
470 nodes.remove(u)
471 path = [u]
472 while p >= 0:
473 path.append(p)
474 p = paths[p]
475 path.reverse()
476 yield path
477
479 '''
480 return one of the shortest path linking this
481 node to specified node.
482
483 @param node: a node label or None
484 @param index: a node index or None. the parameter index
485 has a priority on the parameter node.
486 @type index: int
487
488 @return: list of node index corresponding to the path or None
489 if no path exists.
490 @rtype: list of int or None
491 '''
492 if index is None:
493 index=self.graph.getNode(node).index
494 for p in self.shortestPathIterator([index]):
495 return p
496
497
499 '''
500 return the value of the <key> property of this node
501
502 @param key: the name of a property
503 @type key: str
504 '''
505 return self.__graph._node_attrs.get(self.index,{})[key]
506
508 '''
509 set the value of a node property. In the property doesn't
510 already exist a new property is added to this node.
511
512 @param key: the name of a property
513 @type key: str
514 @param value: the value of the property
515
516 @see: L{Node.__getitem__}
517 '''
518 if self.index in self.__graph._node_attrs:
519 data = self.__graph._node_attrs[self.index]
520 data[key]=value
521 else:
522 self.graph._node_attrs[self.index]={key:value}
523
525 '''
526 Count neighbour of this node
527
528 @rtype: int
529
530 @see: L{Node.degree}
531 '''
532 return len(self.__graph._node[self.index])
533
535 '''
536 iterate through neighbour of this node
537
538 @rtype: iterator in L{Node} instances
539
540 @see: L{Node.neighbourIterator}
541 '''
542 return self.neighbourIterator()
543
546
548
549 if self.index in self.__graph._node_attrs:
550 keys = " ".join(['%s="%s"' % (x[0],str(x[1]).replace('"','\\"').replace('\n','\\n'))
551 for x in self.__graph._node_attrs[self.index].iteritems()]
552 )
553 else:
554 keys=''
555
556 return '%d [label="%s" %s]' % (self.index,str(self.label).replace('"','\\"').replace('\n','\\n'),keys)
557
558 label = property(getLabel, None, None, "Label of the node")
559
560 graph = property(getGraph, None, None, "Graph owning this node")
561
562
563
566 self.node1 = node1
567 self.node2 = node2
568
570 return self.node1.graph
571
573 '''
574 test is the edge instance has a property named 'key'.
575
576 @param key: the name of a property
577 @type key: str
578
579 @return: True if the edge has a property named <key>
580 @rtype: bool
581 '''
582 if (self.node1.index,self.node2.index) in self.graph._edge_attrs:
583 return key in self.graph._edge_attrs[(self.node1.index,self.node2.index)]
584 else:
585 return False
586
587
589 return self.node1.graph._directed
590
592 return self.graph._edge_attrs.get((self.node1.index,self.node2.index),{})[key]
593
595 e = (self.node1.index,self.node2.index)
596 if e in self.graph._edge_attrs:
597 data = self.graph._edge_attrs[e]
598 data[key]=value
599 else:
600 self.graph._edge_attrs[e]={key:value}
601
603 e = (self.node1.index,self.node2.index)
604 if e in self.graph._edge_attrs:
605 keys = "[%s]" % " ".join(['%s="%s"' % (x[0],str(x[1]).replace('"','\\"'))
606 for x in self.graph._edge_attrs[e].iteritems()]
607 )
608 else:
609 keys = ""
610
611 if self.directed:
612 link='->'
613 else:
614 link='--'
615
616 return "%d %s %d %s" % (self.node1.index,link,self.node2.index,keys)
617
620
621
622 graph = property(getGraph, None, None, "Graph owning this edge")
623
624 directed = property(getDirected, None, None, "Directed's Docstring")
625
626
628 - def __init__(self,label='G',indexer=None,nodes=None,edges=None):
629 '''
630 Directed Graph constructor.
631
632 @param label: Graph name, set to 'G' by default
633 @type label: str
634 @param indexer: node label indexer
635 @type indexer: Indexer instance
636 @param nodes: set of nodes to add to the graph
637 @type nodes: iterable value
638 @param edges: set of edges to add to the graph
639 @type edges: iterable value
640 '''
641
642 Graph.__init__(self, label, True, indexer, nodes, edges)
643
645 - def __init__(self,label='G',indexer=None,nodes=None,edges=None):
646 '''
647 Directed Graph constructor.
648
649 @param label: Graph name, set to 'G' by default
650 @type label: str
651 @param indexer: node label indexer
652 @type indexer: Indexer instance
653 @param nodes: set of nodes to add to the graph
654 @type nodes: iterable value
655 @param edges: set of edges to add to the graph
656 @type edges: iterable value
657 '''
658
659 Graph.__init__(self, label, False, indexer, nodes, edges)
660
661
662
664 def selectEdge(e):
665 return attribut in e and e[attribut]==value
666 return selectEdge
667