|
34 | 34 |
|
35 | 35 | where x and y will be the two endpoints of the edge (ordered as |
36 | 36 | tail, head in the case of directed edges). |
| 37 | +
|
| 38 | +IMPORTANT NOTE: New code should use the standard nextworkx module |
| 39 | +rather that the classes here. |
| 40 | +
|
37 | 41 | """ |
38 | 42 | from collections import deque |
| 43 | +from .presentations import CyclicList |
39 | 44 |
|
40 | 45 | try: |
41 | 46 | import sage.all |
|
52 | 57 | pass |
53 | 58 |
|
54 | 59 |
|
55 | | -class CyclicList(list): |
56 | | - def __getitem__(self, n): |
57 | | - if isinstance(n, int): |
58 | | - return list.__getitem__(self, n % len(self)) |
59 | | - elif isinstance(n, slice): |
60 | | - # Python3 only: in python2, __getslice__ gets called instead. |
61 | | - return list.__getitem__(self, n) |
62 | | - |
63 | | - def succ(self, x): |
64 | | - return self[(self.index(x) + 1) % len(self)] |
65 | | - |
66 | | - def pred(self, x): |
67 | | - return self[(self.index(x) - 1) % len(self)] |
68 | | - |
69 | 60 |
|
70 | 61 | class BaseEdge(tuple): |
71 | 62 | """ |
@@ -394,112 +385,6 @@ def is_connected(self, deleted_vertices=[]): |
394 | 385 | """ |
395 | 386 | return len(self.components(deleted_vertices)) <= 1 |
396 | 387 |
|
397 | | - def one_min_cut(self, source, sink, capacity=None): |
398 | | - """ |
399 | | - Find one minimal cut which separates source from sink, using |
400 | | - the classical Ford-Fulkerson algorithm. |
401 | | -
|
402 | | - Returns a dict containing the set of vertices on the source |
403 | | - side of the cut, the set of edges that cross the cut, a |
404 | | - maximal family of weighted edge-disjoint paths from source to |
405 | | - sink, the set of edges with non-zero residual, and the |
406 | | - associated maximum flow. |
407 | | -
|
408 | | - The edge capacities are supplied as a dictionary, with |
409 | | - edges as keys and the capacity of the edge as value. If |
410 | | - no capacity dict is supplied, every edge is given capacity |
411 | | - 1. Edges omitted from the capacity dict have infinite |
412 | | - capacity. |
413 | | -
|
414 | | - When called as a Graph method, the flow is relative to the |
415 | | - implicit orientation of an undirected edge e from e[0] to |
416 | | - e[1]. (This is determined when the edge is first constructed |
417 | | - from a pair of vertices.) A negative flow value means the flow |
418 | | - direction is from e[1] to e[0]. When called as a DiGraph |
419 | | - method, paths are directed and flows go in the direction of |
420 | | - the directed edge. |
421 | | -
|
422 | | - >>> caps = {('s',0):3,('s',1):2,(0,1):2,(0,'t'):4,(1,'t'):1} |
423 | | - >>> G = Graph(caps.keys()) |
424 | | - >>> cap_dict = dict((e, caps[tuple(e)]) for e in G.edges) |
425 | | - >>> flow = G.one_min_cut('s', 't', cap_dict)['flow'] |
426 | | - >>> [flow[e] for e in sorted(G.edges, key=str)] |
427 | | - [-1, 4, 1, 3, 2] |
428 | | - >>> G = Digraph(caps.keys()) |
429 | | - >>> cap_dict = dict((e, caps[tuple(e)]) for e in G.edges) |
430 | | - >>> flow = G.one_min_cut('s', 't', cap_dict)['flow'] |
431 | | - >>> [flow[e] for e in sorted(G.edges, key=str)] |
432 | | - [0, 3, 1, 3, 1] |
433 | | - """ |
434 | | - if sink == source: |
435 | | - return None |
436 | | - if capacity is None: |
437 | | - residual = dict.fromkeys(self.edges, 1) |
438 | | - else: |
439 | | - residual = dict.copy(capacity) |
440 | | - for e in self.edges: |
441 | | - if e not in residual: |
442 | | - residual[e] = float('inf') |
443 | | - full_edges = {e for e in self.edges if residual[e] == 0} |
444 | | - children = {} |
445 | | - for vertex in self.vertices: |
446 | | - children[vertex] = set() |
447 | | - cut_set = set() |
448 | | - path_list = [] |
449 | | - while True: |
450 | | - # Try to find a new path from source to sink |
451 | | - parents, cut_set, reached_sink = {}, {source}, False |
452 | | - generator = self.breadth_first_edges( |
453 | | - source=source, |
454 | | - forbidden=full_edges, |
455 | | - for_flow=True) |
456 | | - for parent, vertex, child in generator: |
457 | | - parents[child] = (parent, vertex) |
458 | | - cut_set.add(child(vertex)) |
459 | | - if child(vertex) == sink: |
460 | | - reached_sink = True |
461 | | - break |
462 | | - # If we did not get to the sink, we visited every vertex |
463 | | - # reachable from the source, thereby providing the cut |
464 | | - # set. |
465 | | - if not reached_sink: |
466 | | - break |
467 | | - # If we got to the sink, do the bookkeeping and continue. |
468 | | - path = deque() |
469 | | - flow = residual[child] |
470 | | - while True: |
471 | | - path.appendleft((vertex, child)) |
472 | | - children[vertex].add(child) |
473 | | - if vertex == source: |
474 | | - break |
475 | | - child = parent |
476 | | - parent, vertex = parents[child] |
477 | | - flow = min(flow, residual[child]) |
478 | | - for vertex, edge in path: |
479 | | - residual[edge] -= flow |
480 | | - if residual[edge] == 0: |
481 | | - full_edges.add(edge) |
482 | | - path_list.append((flow, path)) |
483 | | - # Find the cut edges. |
484 | | - cut_edges = set() |
485 | | - for vertex in cut_set: |
486 | | - cut_edges |= {edge for edge in self.edges |
487 | | - if vertex in edge |
488 | | - and edge(vertex) not in cut_set} |
489 | | - # Find the unsaturated edges. |
490 | | - unsaturated = [e for e in self.edges if residual[e] > 0] |
491 | | - flow_dict = dict.fromkeys(self.edges, 0) |
492 | | - # Compute the flow. |
493 | | - for flow, edges in path_list: |
494 | | - for vertex, edge in edges: |
495 | | - if vertex == edge[0]: |
496 | | - flow_dict[edge] += flow |
497 | | - else: |
498 | | - flow_dict[edge] -= flow |
499 | | - return {'set': cut_set, 'edges': cut_edges, 'paths': path_list, |
500 | | - 'residuals': residual, 'unsaturated': unsaturated, |
501 | | - 'flow': flow_dict} |
502 | | - |
503 | 388 | def reduced(self): |
504 | 389 | R = ReducedGraph() |
505 | 390 | for e in self.edges: |
@@ -551,9 +436,9 @@ def mergeable(self): |
551 | 436 |
|
552 | 437 | def to_networkx(self): |
553 | 438 | """ |
554 | | - Return a copy of the graph in the networkx format |
| 439 | + Return a copy of the graph in the networkx format. |
555 | 440 | """ |
556 | | - G = nx.Graph() |
| 441 | + G = nx.MultiGraph() |
557 | 442 | G.add_nodes_from(self.vertices) |
558 | 443 | G.add_edges_from(self.edges) |
559 | 444 | return G |
@@ -625,12 +510,6 @@ def embedding(self): |
625 | 510 | if self.is_planar(): |
626 | 511 | return self._embedding |
627 | 512 |
|
628 | | - def one_min_cut(self, source, sink): |
629 | | - capacity = {e: e.multiplicity for e in self.edges} |
630 | | - cut = Graph.one_min_cut(self, source, sink, capacity) |
631 | | - cut['size'] = sum(e.multiplicity for e in cut['edges']) |
632 | | - return cut |
633 | | - |
634 | 513 | def cut_pairs(self): |
635 | 514 | """ |
636 | 515 | Return a list of cut_pairs. The graph is assumed to be |
@@ -940,7 +819,7 @@ def DAG(self): |
940 | 819 | edges.add((dag_tail, dag_head)) |
941 | 820 | return Digraph(edges, self.components) |
942 | 821 |
|
943 | | - |
| 822 | + |
944 | 823 | if _within_sage: |
945 | 824 | def _to_sage(self, loops=True, multiedges=True): |
946 | 825 | S = sage.graphs.graph.Graph(loops=loops, multiedges=multiedges) |
|
0 commit comments