def test_arrival_departure_dfs(self): disjoint_graph = { "graph": { "a": ["b", "c"], "b": [], "c": ["d", "e"], "d": ["b", "f"], "e": ["f"], "f": [], "g": ["h"], "h": [] }, "directed": True } graph = Graph(input_graph=json.dumps(disjoint_graph)) # list to store the arrival time of vertex arrival = {v: 0 for v in graph.vertices()} # list to store the departure time of vertex departure = {v: 0 for v in graph.vertices()} # mark all the vertices as not discovered discovered = {v: False for v in graph.vertices()} time = -1 for v in graph.vertices(): if not discovered[v]: time = arrival_departure_dfs(graph, v, discovered, arrival, departure, time) # pair up the arrival and departure times and ensure correct ordering result = list(zip(arrival.values(), departure.values())) expected_times = [(0, 11), (1, 2), (3, 10), (4, 7), (8, 9), (5, 6), (12, 15), (13, 14)] self.assertListEqual(expected_times, result)
def test_add_edge(self): graph = Graph("my graph") graph.add_vertex("A") graph.add_vertex("B") graph.add_edge("A", "B") self.assertEqual(1, len(graph.edges())) graph.add_edge("X", "Y") self.assertEqual(2, len(graph.edges())) self.assertEqual(4, len(graph.vertices()))
def density(graph: Graph) -> float: """ The graph density is defined as the ratio of the number of edges of a given graph, and the total number of edges, the graph could have. A dense graph is a graph G = (V, E) in which |E| = Θ(|V|^2) :param graph: :return: """ V = len(graph.vertices()) E = len(graph.edges()) return 2.0 * (E / (V**2 - V))
def check_for_cycles(graph: Graph, v: str, visited: Dict[str, bool], rec_stack: List[bool]) -> bool: """ :param graph: :param v: vertex to start from :param visited: list of visited vertices :param rec_stack: :return: whether or not the graph contains a cycle """ visited[v] = True rec_stack[graph.vertices().index(v)] = True for neighbour in graph[v]: if not visited.get(neighbour, False): if check_for_cycles(graph, neighbour, visited, rec_stack): return True elif rec_stack[graph.vertices().index(neighbour)]: return True rec_stack[graph.vertices().index(v)] = False return False
def topological(graph: Graph) -> List[str]: """ O(V+E) :param graph: :return: List of vertices sorted topologically """ def mark_visited(g: Graph, v: str, v_map: Dict[str, bool], t_sort_results: List[str]): v_map[v] = True for n in g.get_neighbors(v): if not v_map[n]: mark_visited(g, n, v_map, t_sort_results) t_sort_results.append(v) visited = {v: False for v in graph.vertices()} result: List[str] = [] for v in graph.vertices(): if not visited[v]: mark_visited(graph, v, visited, result) # Contains topo sort results in reverse order return result[::-1]
def diameter(graph: Graph) -> int: """ :param graph: :return: length of longest path in graph """ vee = graph.vertices() pairs = [(vee[i], vee[j]) for i in range(len(vee) - 1) for j in range(i + 1, len(vee))] smallest_paths = [] for (start, end) in pairs: paths = find_all_paths(graph, start, end) smallest = sorted(paths, key=len)[0] smallest_paths.append(smallest) smallest_paths.sort(key=len) # longest path is at the end of list, # i.e. diameter corresponds to the length of this path dia = len(smallest_paths[-1]) - 1 return dia
def is_complete(graph: Graph) -> bool: """ Checks that each vertex has V(V-1) / 2 edges and that each vertex is connected to V - 1 others. runtime: O(n^2) :param graph: :return: true or false """ V = len(graph.vertices()) max_edges = (V**2 - V) if not graph.is_directed(): max_edges //= 2 E = len(graph.edges()) if E != max_edges: return False for vertex in graph: if len(graph[vertex]) != V - 1: return False return True
def breadth_first_search(graph: Graph, start: str) -> List[str]: """ :param graph: :param start: the vertex to start the traversal from :return: """ # Mark all the vertices as not visited visited = {k: False for k in graph.vertices()} # Mark the start vertices as visited and enqueue it visited[start] = True queue = [start] walk = [] while queue: cur = queue.pop(0) walk.append(cur) for i in graph[cur]: if not visited[i]: queue.append(i) visited[i] = True return walk
def is_connected(graph: Graph, start_vertex: str = None, vertices_encountered: Set[str] = None) -> bool: """ :param graph: :param start_vertex: :param vertices_encountered: :return: whether or not the graph is connected """ if vertices_encountered is None: vertices_encountered = set() vertices = graph.vertices() if not start_vertex: # choose a vertex from graph as a starting point start_vertex = vertices[0] vertices_encountered.add(start_vertex) if len(vertices_encountered) != len(vertices): for vertex in graph[start_vertex]: if vertex not in vertices_encountered: if is_connected(graph, vertex, vertices_encountered): return True else: return True return False
def test_set_from_adjacency_matrix(self): array_graph = np.array([[0, 1], [1, 0]], dtype=object) graph = Graph(input_array=array_graph) self.assertEqual(2, len(graph.vertices())) self.assertEqual(1, len(graph.edges()))
def test_add_vertex(self): graph = Graph("my graph") graph.add_vertex("A") self.assertEqual(['A'], graph.vertices())