|
1 | 1 | from .utils import * |
2 | 2 | from .vector import Vector |
3 | 3 | import random |
4 | | -from typing import TypeVar, Callable |
| 4 | +import itertools |
| 5 | +from typing import Dict, Iterable, List, Tuple, TypeVar, Callable, Union |
5 | 6 |
|
6 | | -__all__ = ["Edge", "Graph"] |
| 7 | +__all__ = ["Edge", "Graph", "SwitchGraph"] |
7 | 8 |
|
8 | 9 |
|
9 | 10 | class Edge: |
@@ -34,6 +35,90 @@ def unweighted_edge(edge): |
34 | 35 | return '%d %d' % (edge.start, edge.end) |
35 | 36 |
|
36 | 37 |
|
| 38 | +class SwitchGraph: |
| 39 | + """A graph which can switch edges quickly |
| 40 | + """ |
| 41 | + directed: bool |
| 42 | + __edges: Dict[Tuple[int, int], int] |
| 43 | + |
| 44 | + def get_edges(self): |
| 45 | + ret: List[Tuple[int, int]] = [] |
| 46 | + for k in self.__edges: |
| 47 | + ret.extend(itertools.repeat(k, self.__edges[k])) |
| 48 | + return ret |
| 49 | + |
| 50 | + def __insert(self, u: int, v: int): |
| 51 | + if (u, v) not in self.__edges: |
| 52 | + self.__edges[(u, v)] = 0 |
| 53 | + self.__edges[(u, v)] += 1 |
| 54 | + |
| 55 | + def __remove(self, u: int, v: int): |
| 56 | + self.__edges[(u, v)] -= 1 |
| 57 | + if self.__edges[(u, v)] == 0: |
| 58 | + self.__edges.pop((u, v)) |
| 59 | + |
| 60 | + def insert(self, u: int, v: int): |
| 61 | + """Add edge (u, v) |
| 62 | + """ |
| 63 | + self.__insert(u, v) |
| 64 | + if not self.directed and u != v: |
| 65 | + self.__insert(v, u) |
| 66 | + |
| 67 | + def remove(self, u: int, v: int): |
| 68 | + """Remove edge (u, v) |
| 69 | + """ |
| 70 | + self.__remove(u, v) |
| 71 | + if not self.directed and u != v: |
| 72 | + self.__remove(v, u) |
| 73 | + |
| 74 | + def __init__(self, |
| 75 | + E: Iterable[Union[Edge, Tuple[int, int]]], |
| 76 | + directed: bool = True): |
| 77 | + self.directed = directed |
| 78 | + self.__edges = {} |
| 79 | + for e in E: |
| 80 | + if isinstance(e, Edge): |
| 81 | + self.__insert(e.start, e.end) |
| 82 | + else: |
| 83 | + self.__insert(e[0], e[1]) |
| 84 | + |
| 85 | + def switch(self, *, self_loop: bool = False, repeated_edges: bool = False): |
| 86 | + """Mutates the current directed graph by swapping pairs of edges, without impacting the degree sequence. |
| 87 | +
|
| 88 | + A switch is a general term for a small change in the structure of a graph, achieved by swapping small numbers |
| 89 | + of edges. |
| 90 | +
|
| 91 | + Returns: |
| 92 | + If a switch was performed, then return True. If the switch was rejected, then return False. |
| 93 | + """ |
| 94 | + first, second = random.choices(list(self.__edges.keys()), |
| 95 | + list(self.__edges.values()), |
| 96 | + k=2) |
| 97 | + x1, y1 = first |
| 98 | + x2, y2 = second |
| 99 | + |
| 100 | + if self_loop: |
| 101 | + if x1 == x2 or y1 == y2: |
| 102 | + return False |
| 103 | + else: |
| 104 | + if {x1, y1} & {x2, y2} != set(): |
| 105 | + return False |
| 106 | + |
| 107 | + if repeated_edges: |
| 108 | + if (x1, y2) in self.__edges or (x2, y1) in self.__edges: |
| 109 | + return False |
| 110 | + |
| 111 | + self.remove(x1, y1) |
| 112 | + self.insert(x1, y2) |
| 113 | + self.remove(x2, y2) |
| 114 | + self.insert(x2, y1) |
| 115 | + |
| 116 | + return True |
| 117 | + |
| 118 | + def __iter__(self): |
| 119 | + return iter(self.__edges) |
| 120 | + |
| 121 | + |
37 | 122 | class Graph: |
38 | 123 | """Class Graph: A class of the graph |
39 | 124 | """ |
|
0 commit comments