Exemplo n.º 1
0
 def test_get_adjacency_matrix(self):
     json_graph = {"graph": {"A": ["B"], "B": []}}
     graph = Graph(input_graph=json.dumps(json_graph))
     matrix = graph.get_adjacency_matrix()
     answer = np.array([[0, 1], [0, 0]])
     np.testing.assert_equal(matrix, answer)
     self.assertEqual(answer.size, matrix.size)
Exemplo n.º 2
0
def is_sparse(graph: Graph) -> bool:
    """
    Checks if |E| <= |V^2| / 2
    :param graph:
    :return:
    """
    return graph.size() <= (graph.order()**2 / 2)
Exemplo n.º 3
0
 def test_is_complete(self):
     graph = Graph(input_graph=json.dumps(self.complete_graph))
     self.assertTrue(is_complete(graph))
     graph = Graph(input_graph=json.dumps(self.isolated_graph))
     self.assertFalse(is_complete(graph))
     json_graph = {"name": "", "graph": {"A": ["B"], "B": []}}
     graph = Graph(input_graph=json.dumps(json_graph))
     self.assertFalse(is_complete(graph))
Exemplo n.º 4
0
 def test_read_graph_from_file(self):
     json_graph = {"label": "my graph", "graph": {"A": ["B"], "B": []}}
     with open(path.join(self.test_dir, 'test.txt'), 'w') as out_file:
         out_file.write(json.dumps(json_graph))
     graph = Graph(input_file=str(path.join(self.test_dir, 'test.txt')))
     self.assertEqual(json_graph["label"], graph.get_label())
     self.assertEqual(False, graph.is_directed())
     self.assertEqual(json_graph["graph"], graph.get_graph())
Exemplo n.º 5
0
    def test_density(self):
        graph = Graph(input_graph=json.dumps(self.connected_graph))
        self.assertAlmostEqual(0.4666666666666667, density(graph))

        graph = Graph(input_graph=json.dumps(self.complete_graph))
        self.assertEqual(1.0, density(graph))

        graph = Graph(input_graph=json.dumps(self.isolated_graph))
        self.assertEqual(0.0, density(graph))
Exemplo n.º 6
0
def get_complement(graph: Graph) -> Graph:
    """
    If graph is represented as a matrix, invert that matrix
    :param graph:
    :return: inversion of graph
    """
    adj = graph.get_adjacency_matrix()
    complement = invert(adj)
    return Graph(label=f"{graph.get_label()} complement",
                 input_array=complement)
Exemplo n.º 7
0
    def test_save_to_json(self):
        answer = "{\"label\": \"my graph\", \"directed\": false," \
                 " \"graph\": {\"A\": [\"B\"], \"B\": []}}"
        json_graph = {"label": "my graph", "graph": {"A": ["B"], "B": []}}
        graph = Graph(input_graph=json.dumps(json_graph))
        save_to_json(graph, self.test_dir)

        outfile = path.join(self.test_dir, graph.get_label() + ".json")
        with open(outfile) as dot_file:
            dot_lines = "".join(dot_file.readlines())
        self.assertEqual(dot_lines, answer)
Exemplo n.º 8
0
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))
Exemplo n.º 9
0
 def test_complete_digraph(self):
     graph = Graph(input_graph=json.dumps(self.complete_digraph))
     self.assertTrue(is_complete(graph))
     json_graph = {
         "name": "",
         "directed": True,
         "graph": {
             "A": ["B"],
             "B": []
         }
     }
     graph = Graph(input_graph=json.dumps(json_graph))
     self.assertFalse(is_complete(graph))
Exemplo n.º 10
0
def save_to_dot(graph: Graph, out_dir: str):
    """

    :param graph: the graph to render in dot
    :param out_dir: the absolute path of the gv file to write
    :return:
    """
    if not graph.is_directed():
        dot = GraphViz(comment=graph.get_label())
        for node in graph:
            dot.node(node, node)
            for edge in graph[node]:
                dot.edge(node, edge)

        dot.render(path.join(out_dir, f"{graph.get_label()}.gv"), view=False)
Exemplo n.º 11
0
    def test_str(self):
        answer = """my graph
A -> B
B -> 0"""
        json_graph = {"label": "my graph", "graph": {"A": ["B"], "B": []}}
        graph = Graph(input_graph=json.dumps(json_graph))
        self.assertEqual(answer, str(graph))
Exemplo n.º 12
0
def arrival_departure_dfs(graph: Graph,
                          v: str,
                          discovered: Dict[str, bool],
                          arrival: Dict[str, int],
                          departure: Dict[str, int],
                          time: int) -> int:
    """
    Method for DFS with arrival and departure times for each vertex

    O(V+E) -- E could be as big as V^2

    :param graph:
    :param v:
    :param discovered:
    :param arrival:
    :param departure:
    :param time: should be initialized to -1
    :return:
    """
    time += 1

    # when did we arrive at vertex 'v'?
    arrival[v] = time
    discovered[v] = True

    for n in graph.get_neighbors(v):
        if not discovered.get(n, False):
            time = arrival_departure_dfs(graph, n, discovered, arrival, departure, time)

    time += 1
    # increment time and then set departure
    departure[v] = time
    return time
Exemplo n.º 13
0
    def test_save_to_graphviz(self):
        answer = """// my graph
graph {
	A [label=A]
	A -- B
	B [label=B]
}
"""
        json_graph = {"label": "my graph", "graph": {"A": ["B"], "B": []}}
        graph = Graph(input_graph=json.dumps(json_graph))
        save_to_dot(graph, self.test_dir)

        outfile = path.join(self.test_dir, graph.get_label() + ".gv")
        with open(outfile) as dot_file:
            dot_lines = "".join(dot_file.readlines())
        self.assertEqual(dot_lines, answer)
Exemplo n.º 14
0
 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)
Exemplo n.º 15
0
def save_to_json(graph: Graph, out_dir):
    """

    :param graph: the graph to write to json
    :param out_dir: the absolute path to the dir to write the file
    :return:
    """
    g_dict = {
        "label": graph.get_label(),
        "directed": graph.is_directed(),
        "graph": graph.get_graph()
    }

    with open(path.join(out_dir, f"{graph.get_label()}.json"),
              'w',
              encoding="utf8") as out:
        out.write(json.dumps(g_dict))
Exemplo n.º 16
0
 def test_is_dag(self):
     cycle_graph = {
         "directed": True,
         "graph": {
             "A": ["B"],
             "B": ["C", "D"],
             "C": [],
             "D": ["E", "A"],  # cycle A -> B -> D -> A
             "E": []
         }
     }
     graph = Graph(input_graph=json.dumps(cycle_graph))
     self.assertFalse(is_dag(graph))
     dag = cycle_graph
     # remove the cycle
     dag["graph"]["D"] = ["E"]
     graph2 = Graph(input_graph=json.dumps(dag))
     self.assertTrue(is_dag(graph2))
Exemplo n.º 17
0
    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)
Exemplo n.º 18
0
 def test_edges(self):
     json_graph = {"label": "my graph", "graph": {"A": ["B"], "B": []}}
     graph = Graph(input_graph=json.dumps(json_graph))
     self.assertEqual(json_graph['label'], graph.get_label())
     self.assertEqual(False, graph.is_directed())
     self.assertEqual(json_graph['graph'], graph.get_graph())
     self.assertEqual([Edge('A', 'B')], graph.edges())
Exemplo n.º 19
0
 def test_find_circuit(self):
     circuit_graph = {
         "directed": True,
         "graph": {
             "A": ["B"],
             "B": ["C"],
             "C": ["A"]  # circuit A -> C -> B -> A
         }
     }
     graph = Graph(input_graph=json.dumps(circuit_graph))
     circuit = find_circuit(graph)
     expected_circuit = ["A", "C", "B", "A"]
     self.assertListEqual(circuit, expected_circuit)
Exemplo n.º 20
0
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
Exemplo n.º 21
0
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
Exemplo n.º 22
0
def is_simple(graph: Graph) -> bool:
    """
    A simple graph has no cycles
    :param graph:
    :return: whether or not the graph is simple
    """
    visited = {k: False for k in graph}
    rec_stack = [False] * graph.order()
    for v in graph:
        if not visited[v]:
            if check_for_cycles(graph, v, visited, rec_stack):
                return False
    return True
Exemplo n.º 23
0
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]
Exemplo n.º 24
0
    def test_topological_sort(self):
        t_sort_graph = {
            "directed": True,
            "graph": {
                "A": [],
                "B": [],
                "C": ["D"],
                "D": ["B"],
                "E": ["A", "B"],
                "F": ["A", "C"]
            }
        }
        graph = Graph(input_graph=json.dumps(t_sort_graph))

        expected_results = ["F", "E", "C", "D", "B", "A"]
        results = topological(graph)
        self.assertListEqual(expected_results, results)
Exemplo n.º 25
0
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
Exemplo n.º 26
0
 def test_find_all_paths(self):
     json_graph = {
         "label": "test2",
         "directed": False,
         "graph": {
             "a": ["d", "f"],
             "b": ["c"],
             "c": ["b", "c", "d", "e"],
             "d": ["a", "c"],
             "e": ["c"],
             "f": ["d"]
         }
     }
     graph = Graph(input_graph=json.dumps(json_graph))
     paths = find_all_paths(graph, "a", "b")
     self.assertListEqual([['a', 'd', 'c', 'b'], ['a', 'f', 'd', 'c', 'b']],
                          paths)
     paths = find_all_paths(graph, "z", "b")
     self.assertListEqual([], paths)
    def test_iterator(self):
        json_graph = {
            "name": "my graph",
            "graph": {
                "A": ["B", "C", "D"],
                "B": [],
                "C": [],
                "D": []
            }
        }
        graph = Graph(input_graph=json.dumps(json_graph))

        iterations = 0
        for key in graph:
            iterations += 1
            if key == "A":
                self.assertEqual(len(graph[key]), 3)
            else:
                self.assertEqual(len(graph[key]), 0)
        self.assertEqual(iterations, 4)
Exemplo n.º 28
0
 def test_find_path(self):
     json_graph = {
         "label": "test",
         "directed": False,
         "graph": {
             "a": ["d"],
             "b": ["c"],
             "c": ["b", "c", "d", "e"],
             "d": ["a", "c"],
             "e": ["c"],
             "f": []
         }
     }
     graph = Graph(input_graph=json.dumps(json_graph))
     path = find_path(graph, "a", "b")
     self.assertListEqual(['a', 'd', 'c', 'b'], path)
     path = find_path(graph, "a", "f")
     self.assertListEqual([], path)
     path = find_path(graph, "c", "c")
     self.assertListEqual(['c'], path)
     path = find_path(graph, "z", "a")
     self.assertListEqual([], path)
Exemplo n.º 29
0
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
Exemplo n.º 30
0
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