| 10361 | ### Orientations |
| 10362 | |
| 10363 | def strong_orientation(self): |
| 10364 | r""" |
| 10365 | Returns a strongly connected orientation of the current graph. |
| 10366 | ( cf. http://en.wikipedia.org/wiki/Strongly_connected_component ) |
| 10367 | |
| 10368 | An orientation of a an undirected graph is a digraph obtained by |
| 10369 | giving an unique direction to each of its edges. An orientation |
| 10370 | is said to be strong if there is a directed path between each |
| 10371 | pair of vertices. |
| 10372 | |
| 10373 | If the graph is 2-edge-connected, a strongly connected orientation |
| 10374 | can be found in linear time. If the given graph is not 2-connected, |
| 10375 | the orientation returned will ensure that each 2-connected component |
| 10376 | has a strongly connected orientation. |
| 10377 | |
| 10378 | OUTPUT: |
| 10379 | |
| 10380 | A digraph representing an orientation of the current graph. |
| 10381 | |
| 10382 | NOTES: |
| 10383 | |
| 10384 | - This method assumes the graph is connected. |
| 10385 | - This algorithm works in O(m). |
| 10386 | |
| 10387 | EXAMPLE: |
| 10388 | |
| 10389 | For a 2-regular graph, a strong orientation gives to each vertex |
| 10390 | an out-degree equal to 1:: |
| 10391 | |
| 10392 | sage: g = graphs.CycleGraph(5) |
| 10393 | sage: g.strong_orientation().out_degree() |
| 10394 | [1, 1, 1, 1, 1] |
| 10395 | |
| 10396 | The Petersen Graph is 2-edge connected. It then has a strongly |
| 10397 | connected orientation:: |
| 10398 | |
| 10399 | sage: g = graphs.PetersenGraph() |
| 10400 | sage: o = g.strong_orientation() |
| 10401 | sage: len(o.strongly_connected_components()) |
| 10402 | 1 |
| 10403 | |
| 10404 | The same goes for the CubeGraph in any dimension :: |
| 10405 | |
| 10406 | sage: for i in range(2,5): |
| 10407 | ... g = graphs.CubeGraph(i) |
| 10408 | ... o = g.strong_orientation() |
| 10409 | ... len(o.strongly_connected_components()) |
| 10410 | 1 |
| 10411 | 1 |
| 10412 | 1 |
| 10413 | |
| 10414 | """ |
| 10415 | |
| 10416 | d = DiGraph(multiedges=self.allows_multiple_edges()) |
| 10417 | |
| 10418 | id = {} |
| 10419 | i = 0 |
| 10420 | |
| 10421 | # The algorithm works through a depth-first search. Any edge |
| 10422 | # used in the depth-first search is oriented in the direction |
| 10423 | # in which it has been used. All the other edges are oriented |
| 10424 | # backward |
| 10425 | |
| 10426 | v = self.vertex_iterator().next() |
| 10427 | seen = {} |
| 10428 | i=1 |
| 10429 | |
| 10430 | # Time at which the vertices have been discovered |
| 10431 | seen[v] = i |
| 10432 | |
| 10433 | # indicates the stack of edges to explore |
| 10434 | next = self.edges_incident(v) |
| 10435 | |
| 10436 | while next: |
| 10437 | e = next.pop(-1) |
| 10438 | # We assume e[0] to be a `seen` vertex |
| 10439 | e = e if seen.get(e[0],False) != False else (e[1],e[0],e[2]) |
| 10440 | |
| 10441 | # If we discovered a new vertex |
| 10442 | if seen.get(e[1],False) == False: |
| 10443 | d.add_edge(e) |
| 10444 | next.extend([ee for ee in self.edges_incident(e[1]) if (((e[0],e[1]) != (ee[0],ee[1])) and ((e[0],e[1]) != (ee[1],ee[0])))]) |
| 10445 | i+=1 |
| 10446 | seen[e[1]]=i |
| 10447 | |
| 10448 | # Else, we orient the edges backward |
| 10449 | else: |
| 10450 | if seen[e[0]] < seen[e[1]]: |
| 10451 | d.add_edge((e[1],e[0],e[2])) |
| 10452 | else: |
| 10453 | d.add_edge(e) |
| 10454 | |
| 10455 | # Case of multiple edges. If another edge has already been inserted, we add the new one |
| 10456 | # in the opposite direction. |
| 10457 | tmp = None |
| 10458 | for e in self.multiple_edges(): |
| 10459 | if tmp == (e[0],e[1]): |
| 10460 | if d.has_edge(e[0].e[1]): |
| 10461 | d.add_edge(e[1],e[0],e[2]) |
| 10462 | else: |
| 10463 | d.add_edge(e) |
| 10464 | tmp = (e[0],e[1]) |
| 10465 | |
| 10466 | return d |
| 10467 | |