Beispiel #1
0
	def toDiGraph(self):
		"""
		converts the HyGraph instance to its analogous DiGraph, where each
		hyperedge is replaced by a pair of directed edges between each pair
		of vertices in the hyperedge.
		"""
		ret = DiGraph();
		ret._spm = self._spmT.SpMM(self._spm)
		return ret
Beispiel #2
0
 def __init__(self, graph: DiGraph = None):
     if graph:
         self.graph = graph
     else:
         self.graph = DiGraph()
Beispiel #3
0
class GraphAlgo(GraphAlgoInterface):

    def __init__(self, graph: DiGraph = None):
        self.graph = graph

    """Return the underlying graph of which this class works."""

    def get_graph(self) -> DiGraph:
        return self.graph

    """
    This method load a graph to this graph algorithm.
    if the file was successfully loaded - the underlying graph
    of this class will be changed (to the loaded one), in case the
    graph was not loaded the original graph should remain "as is".
    
    we used json method.
    
    @param file - file name
    @return true - if the graph was successfully loaded.
    """

    def load_from_json(self, file_name: str) -> bool:
        try:
            fp = open(file_name)

            self.graph = DiGraph()
            graph_file = json.load(fp)
            edges = graph_file.get('Edges')
            nodes = graph_file.get('Nodes')

            for n in nodes:
                if n.get('pos') is not None:
                    posarr = str(n.get('pos')).split(",")
                    pos = (float(posarr[0]), float(posarr[1]), 0.0)
                    self.graph.add_node(node_id=n.get('id'), pos=pos)
                else:
                    self.graph.add_node(node_id=n.get('id'))

            for x in edges:
                src = x.get('src')
                dest = x.get('dest')
                w = x.get('w')
                self.graph.add_edge(src, dest, w)
                fp.close()
        except FileExistsError:
            print("Graph was not loaded successfully")
            return False

        return True

    """
    Saves this weighted directed graph to the given file name.
    we used he json method.
    
    @param file - the file name (may include a relative path).
    @return true - if the file was successfully saved
    """

    def save_to_json(self, file_name: str) -> bool:
        if self.graph is None:
            return False

        nodes = []
        for n in self.graph.nodes:
            id = self.graph.nodes.get(n).id
            pos = self.graph.nodes.get(n).pos
            if pos is not None:
                pos_x = self.graph.nodes.get(n).pos[0]
                pos_y = self.graph.nodes.get(n).pos[1]
                pos = str(pos_x) + ',' + str(pos_y) + ',' + str(0.0)
            nodes.append({"pos": pos, "id": id})

        edges = []
        for n in self.graph.nodes:
            for dest in self.graph.nodes.get(n).src:
                w = self.graph.nodes.get(n).src.get(dest)
                edges.append({"src": int(n), "dest": int(dest), "w": w})

        mygraph = {"Edges": edges, "Nodes": nodes}

        with open(file_name, 'w') as json_file:
            json.dump(mygraph, json_file)

        return True

    """

    This method based on Dijkstra Algorithm.
    The data of this algorithm will save in a temporal Node, contains-
        1. The shortest weight from the source vertex to this vertex.
        2. The vertex neighbor that connect this vertex and update him this value of weight accepted.
        (By default the vertex tag contains INF and the vertex parent points as a vertex with key -1).
        
    Returns Tuple contain:
        1. The length of the shortest path between src to dest
        If no such path, returns inf and an empty list
        2. The shortest path between src to dest - as an ordered List of nodes: src--> n1-->n2-->...dest
        If no such path --> returns an empty List.

    The function goes through all the neighbors of the source vertex and does them "weight relief" if necessary-
    That is, if we have reached a vertex whose weight can be reduced relative to its current weight, we will update the information contained the temporal vertex.
    The implementation of the algorithm is done by a priority queue, which is implementation by a function of comparing the weights (tag) of the temporal vertexes in the queue.
    
    """

    def shortest_path(self, id1: int, id2: int) -> (float, list):
        if self.graph is None:
            return (math.inf, [])
        if str(id1) not in self.graph.nodes or str(id2) not in self.graph.nodes:
            return (math.inf, [])

        if id1 == id2:
            return 0, [id1]

        vertex = {}
        for n in self.graph.nodes:
            nodetemp = NodeTemp(n)
            vertex[str(n)] = nodetemp

        vertex.get(str(id1)).tag = 0
        heap = PriorityQueue()
        # heap.__init__(self.graph.v_size())
        heap.put(vertex.get(str(id1)))

        # heapq.heappush(heap, vertex.get(str(id1)))
        flag = True
        while not heap.empty() and flag:
            # heapq.heapify(heap)
            current = heap.get()
            # current = heapq.heappop(heap)
            if current.visit != 1:
                for n in self.graph.get_node(current.idNode).src:
                    b = vertex.get(n)
                    if b.visit != 1:
                        if b.tag > current.tag + self.graph.edges.get(str(current.idNode) + '->' + str(n)):
                            b.tag = current.tag + self.graph.edges.get(str(current.idNode) + '->' + str(n))
                            b.parentId = current.idNode
                    # heapq.heappush(heap, b)
                    heap.put(b)
                current.visit = 1

                if current.idNode == id2:
                    flag = False
        dest = vertex.get(str(id2))  # NodeTemp

        listShort = []
        if dest.tag != math.inf:
            listShort.append(int(dest.idNode))
            curr = dest
            while curr.parentId != -1:
                parent = curr.parentId
                curr = vertex.get(str(parent))
                listShort.append(int(curr.idNode))
            listShort.reverse()
        tup = (dest.tag, listShort)
        return tup

    '''
    Finds the Strongly Connected Component(SCC) that node id1 is a part of.
    @param id1: The node id
    @return: The list of nodes in the SCC
    
    This method is based on the BFS Algorithm- with little changes.
    We will send the node we want find his SSC and do BFS algorithm that will return the list of nodes that we can reach from this node.
    After this we will "reverse" the graph's edges and will send the node to BFS one more time.
    Finally, we merge the lists and return the union.  
    '''

    def connected_component(self, id1: int) -> list:
        if self.graph is None:
            return []

        if str(id1) not in self.graph.nodes:
            return []
        lists = self.bfs(id1)

        # resets all the visited nodes to unvisited
        for n in lists[1]:
            lists[1][n].tag = 0

        return lists[0]

    """
     bfs will pass over the nodes that we can reach from the src node we will send to the function.
     
     @Return list of those nodes.
     
     The function receives a vertex key from which we will perform the test on the graph connected test.
     During the test we will mark the vertices in the graph in the "tag" that each vertex holds.
 
     """

    def bfs(self, id1: int) -> (list, dict):
        myList = []
        list1 = {}
        node = self.graph.get_node(id1)

        queue = deque()
        # ---> first run from src
        node.tag = 2
        list1[node.id] = node
        myList.append(node)

        glob[str(id1)] = id1
        for n in node.src:
            temp = self.graph.get_node(n)
            temp.tag = 1
            list1[temp.id] = temp
            queue.append(temp)

        while queue:
            n = queue.popleft()

            for x in n.src:
                temp2 = self.graph.get_node(x)
                if temp2.tag == 0:
                    temp2.tag = 1
                    list1[temp2.id] = temp2
                    queue.append(temp2)

        for n in node.dest:
            temp = self.graph.get_node(n)
            if temp.tag == 1:
                queue.append(temp)

        # mark src node as visited
        while queue:
            n = queue.popleft()
            if n.tag == 1:
                n.tag = 2
                list1[n.id] = n
                myList.append(n)
                glob[str(n.id)] = n.id
                for x in n.dest:
                    temp2 = self.graph.get_node(x)
                    queue.append(temp2)

        return myList, list1

    """ 
    Finds all the Strongly Connected Component(SCC) in the graph.
    @return: The list all SCC
    """

    def connected_components(self) -> List[list]:
        glob.clear()
        if self.graph is None:
            return []
        listAns = []

        for i in self.graph.nodes:
            if i not in glob:
                listAns.append(self.connected_component(i))

        return listAns

    """
    Plots the graph.
    If the nodes have a position, the nodes will be placed there.
    Otherwise, they will be placed in a random but elegant manner.
    @return: None
    """

    def plot_graph(self) -> None:
        if self.graph is None:
            return

        xlist = []
        ylist = []

        # paints all the nodes in the graph
        for x in self.graph.nodes:
            pos = self.graph.get_node(x).pos
            if pos is None:
                self.graph.nodes.get(x).pos = (random.uniform(0.0, 50), random.uniform(0.0, 50), 0.0)
                pos = self.graph.nodes.get(x).pos
            xlist.append(pos[0])
            ylist.append(pos[1])

            label = "{:}".format(int(x))
            plt.annotate(label, (pos[0], pos[1]), textcoords="offset points", xytext=(2, 3), ha='center',
                         color=[0, 0.75, 0.75])
        plt.plot(xlist, ylist, '.', color='red')

        # loop over edges in the graph and draw them one by one
        for e in self.graph.edges:
            edge = str(e).split('->')
            src = int(edge[0])
            dest = int(edge[1])
            w = self.graph.edges.get(e)
            # draws the arrow pointing in the edge direction --> (dest)
            pos = self.graph.get_node(dest).pos

            xy1 = (pos[0], pos[1])

            # draw the edge coming out from the src node to dest node
            pos = self.graph.get_node(src).pos
            xy2 = (pos[0], pos[1])

            plt.annotate(text="", xy=xy1, xytext=xy2, arrowprops=dict(arrowstyle="->"))

        plt.title("My Graph")

        for key in self.graph.nodes:
            ver = self.graph.nodes.get(key)
            if not ver.info:
                ver.pos = None

        plt.show()
Beispiel #4
0
def graph_creator() -> DiGraph:
    graph = DiGraph()
    for x in range(7):
        graph.add_node(x)

    graph.add_edge(1, 2, 2.5)
    graph.add_edge(1, 3, 1.7)
    graph.add_edge(2, 3, 0.3)
    graph.add_edge(3, 2, 5.6)
    graph.add_edge(5, 1, 3.2)
    graph.add_edge(5, 0, 9.8)
    graph.add_edge(0, 6, 3.8)

    return graph
Beispiel #5
0
 def setUp(self) -> None:
     self.graph = DiGraph()
 def test_get_graph(self):
     graph_d = DiGraph()
     graph_d.add_node(0)
     graph_d.add_node(1)
     graph_d.add_node(2)
     graph_d.add_node(3)
     graph_d.add_node(4)
     graph_d.add_edge(0, 1, 6)
     graph_d.add_edge(0, 2, 9)
     graph_d.add_edge(1, 2, 2)
     graph_d.add_edge(1, 3, 7)
     graph_d.add_edge(1, 4, 5)
     graph_d.add_edge(2, 0, 3)
     graph_d.add_edge(2, 3, 1)
     graph_d.add_edge(3, 4, 1)
     graph_d.add_edge(4, 1, 3)
     graph_a = GraphAlgo(graph_d)
     self.assertEqual(graph_d, graph_a.get_graph())
 def test_load_from_json(self):
     graph_d = DiGraph()
     graph_d.add_node(0)
     graph_d.add_node(1)
     graph_d.add_node(2)
     graph_d.add_node(3)
     graph_d.add_node(4)
     graph_d.add_edge(0, 1, 6)
     graph_d.add_edge(0, 2, 9)
     graph_d.add_edge(1, 2, 2)
     graph_d.add_edge(1, 3, 7)
     graph_d.add_edge(1, 4, 5)
     graph_d.add_edge(2, 0, 3)
     graph_d.add_edge(2, 3, 1)
     graph_d.add_edge(3, 4, 1)
     graph_d.add_edge(4, 1, 3)
     graph_a = GraphAlgo(graph_d)
     self.assertTrue(graph_a.save_to_json("test1.txt"))
     self.assertTrue(graph_a.load_from_json("test1.txt"))
     self.assertFalse(
         graph_a.load_from_json("test2.txt")
     )  # Attempt to load the graph from a non-existent file.
 def test_plot_graph(self):
     graph_d = DiGraph()
     graph_d.add_node(1)
     graph_d.add_node(2)
     graph_d.add_node(3)
     graph_d.add_node(4)
     graph_d.add_node(5)
     graph_d.add_edge(1, 2, 3)
     graph_d.add_edge(2, 1, 4)
     graph_d.add_edge(1, 3, 4)
     graph_d.add_edge(3, 1, 6)
     graph_d.add_edge(3, 5, 1.2)
     graph_d.add_edge(2, 4, 6)
     graph_a = GraphAlgo(graph_d)
     self.assertIsNone(graph_a.plot_graph(graph_a))
Beispiel #9
0
 def test_v_size(self):
     # init and set graph
     graph = DiGraph()
     self.assertEqual(0, graph.v_size())
     graph.add_node(1, (1, 1, 2))
     graph.add_node(2, (2, 2, 2))
     self.assertEqual(2, graph.v_size())
     graph.remove_node(1)
     self.assertEqual(1, graph.v_size())
     graph.remove_node(2)
     self.assertEqual(0, graph.v_size())
Beispiel #10
0
 def test_e_size(self):
     graph = DiGraph()
     graph.add_node(1, (1, 1, 2))
     graph.add_node(2, (2, 2, 2))
     graph.add_node(3, (3, 1, 2))
     graph.add_node(4, (4, 2, 2))
     self.assertEqual(0, graph.e_size())
     graph.add_edge(1, 2, 4)
     graph.add_edge(3, 1, 2.3)
     self.assertEqual(2, graph.e_size())
     graph.remove_edge(1, 2)
     self.assertEqual(1, graph.e_size())
     graph.remove_edge(3, 1)
     self.assertEqual(0, graph.e_size())
Beispiel #11
0
 def test_remove_edge(self):
     graph = DiGraph()
     graph.add_node(1, (1, 1, 2))
     graph.add_node(2, (2, 2, 2))
     graph.add_node(3, (3, 1, 2))
     graph.add_node(4, (4, 2, 2))
     graph.add_edge(1, 2, 4)
     graph.add_edge(3, 1, 2.3)
     graph.add_edge(1, 3, 2.3)
     graph.add_edge(3, 4, 2.3)
     self.assertFalse(graph.remove_edge(0, 3))
     self.assertFalse(graph.remove_edge(3, -1))
     self.assertFalse(graph.remove_edge(1, 4))
     self.assertTrue(graph.remove_edge(1, 3))
     self.assertFalse(graph.remove_edge(1, 3))
Beispiel #12
0
 def test_add_node(self):
     graph = DiGraph()
     graph.add_node(1, (1, 1, 2))
     self.assertFalse(graph.add_node(1, (1, 1, 2)))
     graph.add_node(3, (1, 1, 2))
     self.assertFalse(graph.add_node(3, (0, 2, 3)))
Beispiel #13
0
 def test_add_edge(self):
     graph = DiGraph()
     graph.add_node(1, (1, 1, 2))
     graph.add_node(2, (2, 2, 2))
     graph.add_node(3, (3, 1, 2))
     graph.add_node(4, (4, 2, 2))
     graph.add_edge(1, 2, 4)
     self.assertFalse(graph.add_edge(1, 2, 4))
     graph.add_edge(3, 1, 2.3)
     graph.add_edge(1, 3, 2.3)
     self.assertFalse(graph.add_edge(-1, 2, 1))
Beispiel #14
0
    def test_get_mc(self):
        graph = DiGraph()
        self.assertEqual(0, graph.get_mc())
        graph.add_node(1, (1, 1, 2))
        graph.add_node(2, (2, 2, 2))
        graph.add_node(3, (3, 1, 2))
        graph.add_node(4, (4, 2, 2))
        self.assertEqual(4, graph.get_mc())
        graph.add_edge(1, 2, 4)
        graph.add_edge(3, 1, 2.3)
        graph.add_edge(1, 3, 2.3)
        graph.add_edge(3, 4, 2.3)
        self.assertEqual(8, graph.get_mc())
        graph.remove_edge(1, 2)
        self.assertEqual(9, graph.get_mc())

        graph.remove_node(3)
        self.assertEqual(13, graph.get_mc())
Beispiel #15
0
 def test_plot(self):
     graph = Graph()
     graph2 = Graph()
     for number in range(3):
         graph.add_node(number, (number, number, number))
         graph2.add_node(number, (number, number, number))
     graph.add_edge(0, 1, 1)
     graph2.add_edge(0, 1, 1)
     graph.add_edge(1, 2, 1)
     graph2.add_edge(1, 2, 1)
     graph.add_edge(2, 0, 1)
     graph2.add_edge(2, 0, 1)
     self.assertEqual(graph, graph2)
     algo = Algo(graph)
     algo.plot_graph()
     x = 5
     self.assertEqual(graph, graph2)
Beispiel #16
0
def init():
    g = DiGraph()
    for i in range(10):
        g.add_node(i)
    return g
Beispiel #17
0
    def test_all_out_edges_of_node(self):
        graph = DiGraph()
        node1 = Node(1, (1, 1, 2))
        node2 = Node(2, (2, 2, 2))
        node3 = Node(3, (3, 1, 2))
        node4 = Node(4, (4, 2, 2))
        graph.add_node(node1.getKey(), node1.getLocation())
        graph.add_node(node2.getKey(), node2.getLocation())
        graph.add_node(node3.getKey(), node3.getLocation())
        graph.add_node(node4.getKey(), node4.getLocation())
        graph.add_edge(1, 2, 4)
        graph.add_edge(3, 1, 2.3)
        graph.add_edge(1, 3, 2.3)
        graph.add_edge(3, 4, 2.3)

        res = {}
        res[2] = 4
        res[3] = 2.3
        self.assertEqual(res, graph.all_out_edges_of_node(1))
        res.pop(2)
        res.pop(3)
        res[1] = 2.3
        res[4] = 2.3
        self.assertEqual(res, graph.all_out_edges_of_node(3))
 def test_save_to_json(self):
     graph_d = DiGraph()
     graph_d.add_node(0)
     graph_d.add_node(1)
     graph_d.add_node(2)
     graph_d.add_node(3)
     graph_d.add_node(4)
     graph_d.add_edge(0, 1, 6)
     graph_d.add_edge(0, 2, 9)
     graph_d.add_edge(1, 2, 2)
     graph_d.add_edge(1, 3, 7)
     graph_d.add_edge(1, 4, 5)
     graph_d.add_edge(2, 0, 3)
     graph_d.add_edge(2, 3, 1)
     graph_d.add_edge(3, 4, 1)
     graph_d.add_edge(4, 1, 3)
     graph_a = GraphAlgo(graph_d)
     self.assertTrue(graph_a.save_to_json("test1.txt"))
Beispiel #19
0
class GraphAlgo(GraphAlgoInterface):
    """This abstract class represents an interface of a graph."""
    def __init__(self, my_graph=None):
        self.my_graph = my_graph

    def get_graph(self):
        """
        :return: the directed graph on which the algorithm works on.
        """
        return self.my_graph

    def load_from_json(self, file_name: str) -> bool:
        """
        Loads a graph from a json file.
        @param file_name: The path to the json file
        @returns True if the loading was successful, False o.w.
        """
        try:
            import json
            self.my_graph = DiGraph()
            with open(file_name) as json_file:
                data = json.load(json_file)
                for n in data['Nodes']:
                    self.my_graph.add_node(n['id'])
                for e in data['Edges']:
                    self.my_graph.add_edge(e['src'], e['dest'], e['w'])
        except:
            return False
        return True

    def save_to_json(self, file_name: str) -> bool:
        """
        Saves the graph in JSON format to a file
        @param file_name: The path to the out file
        @return: True if the save was successful, Flase o.w.
        """
        # try:
        import json
        data = {'Nodes': [], 'Edges': []}
        with open(file_name, 'w+') as f:
            for n in self.my_graph.get_all_v().keys():
                data['Nodes'].append({'id': n})
            for n, n_data in self.my_graph.get_all_v().items():
                for edge in n_data.edge_NI():
                    data['Edges'].append({
                        'src': edge.get_src(),
                        'dest': edge.get_dest(),
                        'w': edge.get_weight()
                    })
            f.write(json.dumps(data))
        # except: return False
        return True

    def track_path(self, parent, j, path):
        if parent[j] == -1:
            return
        self.track_path(parent, parent[j], path)
        path.append(j)

    def find_path(self, dist, parent, src, dest):
        for i in range(1, len(dist)):
            if i == dest:
                path = list()
                self.track_path(parent, i, path)
                path.insert(0, src)
                return path

    def shortest_path(self, id1: int, id2: int) -> (float, list):
        """
        Returns the shortest path from node id1 to node id2 using Dijkstra's Algorithm
        @param id1: The start node id
        @param id2: The end node id
        @return: The distance of the path, the path as a list
        Example:
        #>>> from GraphAlgo import GraphAlgo
        #>>> g_algo = GraphAlgo()
        #>>> g_algo.addNode(0)
        #>>> g_algo.addNode(1)
        #>>> g_algo.addNode(2)
        #>>> g_algo.addEdge(0,1,1)
        #>>> g_algo.addEdge(1,2,4)
        #>>> g_algo.shortestPath(0,1)
        #(1, [0, 1])
        #>>> g_algo.shortestPath(0,2)
        #(5, [0, 1, 2])
        More info:
        https://en.wikipedia.org/wiki/Dijkstra's_algorithm
        """
        graph = self.my_graph.get_all_v()
        distances = {vertex: float('Infinity')
                     for vertex in graph.keys()
                     }  ## initial all vertices distances to inf.
        distances[id1] = 0  ## the first vertex is defined as distance 0
        parent = [-1] * len(graph.keys())

        pq = [(0, id1)]
        ## expanding all vertices distance by distance to find the optimal path
        while len(pq) > 0:
            current_distance, current_vertex = heapq.heappop(pq)

            # Nodes can get added to the priority queue multiple times. We only
            # process a vertex the first time we remove it from the priority queue.
            if current_distance > distances[current_vertex]:
                continue

            for neighbor in graph[current_vertex].key_NI():
                distance = current_distance + graph[current_vertex].get_weight(
                    neighbor)

                ## verify the best path to take
                if distance < distances[neighbor]:
                    parent[neighbor] = current_vertex
                    distances[neighbor] = distance
                    heapq.heappush(pq, (distance, neighbor))

        path = self.find_path(distances, parent, id1, id2)
        ## return the exact shortest path from id1 to id2
        return (distances[id2],
                path if distances[id2] != float('Infinity') else [])

    def connected_component(self, id1: int) -> list:
        """
        Finds the Strongly Connected Component(SCC) that node id1 is a part of.
        @param id1: The node id
        @return: The list of nodes in the SCC
        """
        v_set = set()
        sccs = self.connected_components()
        for scc in sccs:
            if id1 in scc:
                for i in scc:
                    v_set.add(i)
        return list(v_set)
        # list = []
        # for node in self.my_graph.get_all_v().values():
        #     if self.shortest_path(id1, node.get_key()) != -1 and self.shortest_path(node.get_key(),id1) :
        #         list.append(node.get_key())
        #
        #     return list

    def connected_components(self):  # -> List[list]:
        """
        Finds all the Strongly Connected Component(SCC) in the graph.
        @return: The list all SCC
        """

        # list = [] #this is an eterativ idea
        # for node in self.my_graph.get_all_v().values():
        #     list1=self.connected_component(node.get_key())
        #     list.append(list1)
        # return list

        def Vorder(v, visited, stack, g, gn):  #this in a recoratin idea
            visited[v] = True  ## mark current vertex as visited
            ## continue to all this vertex adjacents
            for n in gn.keys():
                if not visited[n]:
                    Vorder(n, visited, stack, g, g[n].get_neighbors())
            stack = stack.append(v)

        def dfs_traversal(v, visited, g, gn, scc):
            visited[v] = True  ## mark current vertex as visited
            scc.append(v)
            ## continue to all this vertex adjacents
            for n in gn.keys():
                if not visited[n]:
                    dfs_traversal(n, visited, g, g[n].get_neighbors(), scc)

        def get_Tgraph(g):
            gr = DiGraph()
            for v, v_data in g.items():
                for e in v_data.edge_NI():
                    gr.add_node(e.get_dest())
                    gr.add_node(e.get_src())
                    gr.add_edge(e.get_dest(), e.get_src(), e.get_weight())
            return gr.get_all_v()

        stack = list()  ## contains the visited vertices
        g = self.my_graph.get_all_v()
        visited = [False] * (len(g.keys())
                             )  ## marking not visited vertices for the DFS

        ## determining the vertices stack by their finishing time
        for node_id in g.keys():
            if not visited[node_id]:
                Vorder(node_id, visited, stack, g, g[node_id].get_neighbors())

        gr = get_Tgraph(g)  ## traspose graph
        visited = [False] * (len(
            g.keys()))  ## marking not visited vertices for the transpose DFS

        sccs = list()
        ## processing all veritces in the order they defined by the stack
        while stack:
            i = stack.pop()
            if not visited[i]:
                scc = list()
                if i in gr.keys():
                    dfs_traversal(i, visited, gr, gr[i].get_neighbors(), scc)
                else:
                    scc.append(i)
                if len(scc) > 0:
                    sccs.append(scc)
        return sccs

    def plot_graph(self) -> None:
        """
        Plots the graph.
        If the nodes have a position, the nodes will be placed there.
        Otherwise, they will be placed in a random but elegant manner.
        @return: None
        """
        import networkx as nx
        import matplotlib.pyplot as plt

        G = nx.Graph().to_directed()
        for n, n_data in self.my_graph.get_all_v().items():
            G.add_node(n, pos=n_data.get_pos())
        for n, n_data in self.my_graph.get_all_v().items():
            for edge in n_data.edge_NI():
                G.add_edge(edge.get_src(), edge.get_dest())

        # print("Nodes of graph: ")
        # print(G.nodes())
        # print("Edges of graph: ")
        # print(G.edges())

        nx.draw(G, with_labels=True)
        # plt.savefig("path/to/some/location/name.png")  # save as png
        plt.show()  # display
 def test_shortest_path(self):
     graph_d = DiGraph()
     graph_d.add_node(0)
     graph_d.add_node(1)
     graph_d.add_node(2)
     graph_d.add_node(3)
     graph_d.add_node(4)
     graph_d.add_edge(0, 1, 6)
     graph_d.add_edge(0, 2, 9)
     graph_d.add_edge(1, 2, 2)
     graph_d.add_edge(1, 3, 7)
     graph_d.add_edge(1, 4, 5)
     graph_d.add_edge(2, 0, 3)
     graph_d.add_edge(2, 3, 1)
     graph_d.add_edge(3, 4, 1)
     graph_d.add_edge(4, 1, 3)
     graph_a = GraphAlgo(graph_d)
     self.assertTupleEqual((5, [1, 2, 0]), graph_a.shortest_path(
         1, 0))  # Finds a path between 2 connected nodes.
     self.assertTupleEqual((10, [0, 1, 2, 3, 4]), graph_a.shortest_path(
         0, 4))  # Finds a path between 2 connected nodes.
     self.assertTupleEqual((3, [1, 2, 3]), graph_a.shortest_path(
         1, 3))  # Finds a path between 2 connected nodes.
     self.assertTrue(graph_a.get_graph().remove_edge(2,
                                                     0))  # Removes an edge.
     self.assertTupleEqual(
         (float('inf'), []), graph_a.shortest_path(3, 0)
     )  # Attempt to find a path between 2 nodes which exist in the graph but not connected.
     self.assertTupleEqual(
         (float('inf'), []), graph_a.shortest_path(4, 7)
     )  # Attempt to find a path between a node which exists in the graph and one which not exists in the graph.
     self.assertTupleEqual(
         (float('inf'), []), graph_a.shortest_path(8, 1)
     )  # Attempt to find a path between a node which not exists in the graph and one which exists in the graph.
     self.assertTupleEqual(
         (float('inf'), []), graph_a.shortest_path(13, 11)
     )  # Attempt to find a path between 2 nodes which not exist in the graph.
     self.assertTupleEqual(
         (float('inf'), []), graph_a.shortest_path(3, 3)
     )  # Attempt to find a path between a node in the graph to itself.
     self.assertTupleEqual(
         (float('inf'), []), graph_a.shortest_path(3, 3)
     )  # Attempt to find a path between a node which not exists in the graph to itself.
Beispiel #21
0
 def __init__(self, g=None):
     if g:
         self.graph = g
     else:
         self.graph = DiGraph()
Beispiel #22
0
class TestDiGraph(TestCase):
    def setUp(self) -> None:
        self.graph = DiGraph()

    def test_add_node(self):
        self.graph.add_node(2)
        self.assertEqual(self.graph.v_size(), 1)
        self.graph.add_node(3)
        self.graph.add_node(2)  #Adding an existing node
        self.assertEqual(self.graph.v_size(), 2)
        self.assertFalse(self.graph.add_node(3))

    def test_add_edge(self):
        self.graph.add_node(2)
        self.graph.add_node(3)
        self.graph.add_node(4)
        self.assertEqual(self.graph.e_size(), 0)
        self.graph.add_edge(2, 3, 5.6)
        self.assertEqual(self.graph.e_size(), 1)
        self.graph.add_edge(3, 6, 2.0)
        self.assertEqual(self.graph.e_size(), 2)
        self.assertFalse(self.graph.add_edge(3, 6, 2.0))

    def test_remove_node(self):
        self.graph.add_node(1)
        self.assertEqual(self.graph.v_size(), 1)
        self.graph.remove_node(1)
        self.assertEqual(self.graph.v_size(), 0)
        self.graph.add_node(3)
        self.graph.remove_node(4)  #Removes a not existing node
        self.assertFalse(self.graph.remove_node(4))
        self.assertEqual(self.graph.v_size(), 1)

    def test_remove_edge(self):
        self.graph.add_node(0)
        self.graph.add_node(1)
        self.graph.add_edge(0, 1, 9)
        self.assertEqual(self.graph.e_size(), 1)
        self.graph.remove_edge(0, 1)
        self.assertEqual(self.graph.e_size(), 0)
        self.graph.add_edge(1, 0, 3.5)
        self.assertEqual(self.graph.e_size(), 1)
        self.graph.remove_edge(2, 3)  #Removes a not existing edge
        self.assertFalse(self.graph.remove_edge(2, 3))
        self.assertEqual(self.graph.e_size(), 1)

    def test_get_node(self):
        self.graph.add_node(3)
        self.assertIsNotNone(self.graph.get_node(3))
        try:
            self.graph.get_node(2)
        except:
            print("OK")
        try:
            self.graph.get_node(0)
        except:
            print("OK")

    def test_v_size(self):
        self.graph.add_node(3)
        self.assertEqual(self.graph.v_size(), 1)
        self.graph.add_node(3)  #Adding an existing node
        self.assertEqual(self.graph.v_size(), 1)
        self.graph.add_node(2)
        self.graph.remove_node(2)
        self.assertEqual(self.graph.v_size(), 1)
        self.graph.remove_node(7)  #Removing a not existing node
        self.assertEqual(self.graph.v_size(), 1)

    def test_e_size(self):
        self.graph.add_node(1)
        self.graph.add_node(2)
        self.graph.add_node(3)
        self.graph.add_edge(1, 2, 10)
        self.assertEqual(self.graph.e_size(), 1)
        self.graph.add_edge(1, 3, 10)
        self.assertEqual(self.graph.e_size(), 2)
        self.graph.remove_edge(1, 2)
        self.assertEqual(self.graph.e_size(), 1)
        self.graph.remove_edge(3, 2)  #Removing a not existing edge
        self.assertEqual(self.graph.e_size(), 1)
        self.graph.remove_node(1)
        self.assertEqual(self.graph.e_size(), 1)

    def test_get_all_v(self):
        self.graph.add_node(1)
        self.graph.add_node(2)
        test_data_1 = self.graph.get_node(2)
        self.assertIn(test_data_1, self.graph.get_all_v().values())
        test_data_2 = self.graph.get_node(1)
        self.graph.remove_node(1)
        self.assertNotIn(test_data_2, self.graph.get_all_v().values())

    def test_all_in_edges_of_node(self):
        self.graph.add_node(1)
        self.graph.add_node(2)
        self.graph.add_node(3)
        self.graph.add_edge(1, 2, 8)
        self.graph.add_edge(1, 3, 5)
        self.graph.add_edge(3, 2, 2)
        self.assertEqual(2, len(self.graph.all_in_edges_of_node(2)))
        self.assertEqual(1, len(self.graph.all_in_edges_of_node(3)))
        self.graph.remove_edge(1, 2)
        self.assertEqual(1, len(self.graph.all_in_edges_of_node(2)))

    def test_all_out_edges_of_node(self):
        self.graph.add_node(1)
        self.graph.add_node(2)
        self.graph.add_node(3)
        self.graph.add_edge(1, 2, 8)
        self.graph.add_edge(1, 3, 5)
        self.graph.add_edge(3, 2, 2)
        self.assertEqual(2, len(self.graph.all_out_edges_of_node(1)))
        self.graph.add_edge(2, 1, 8)
        self.assertEqual(2, len(self.graph.all_out_edges_of_node(1)))
        self.graph.remove_node(1)
        self.assertEqual(0, len(self.graph.all_out_edges_of_node(1)))

    def test_get_mc(self):
        self.assertEqual(0, self.graph.get_mc())
        self.graph.add_node(1)
        self.graph.add_node(2)
        self.graph.remove_node(1)
        self.assertEqual(3, self.graph.get_mc())
        self.graph.add_node(1)
        self.graph.add_edge(1, 2, 9)
        self.graph.remove_edge(1, 2)
        self.assertEqual(6, self.graph.get_mc())
Beispiel #23
0
class GraphAlgo:
    def __init__(self, g=None):
        if g:
            self.graph = g
        else:
            self.graph = DiGraph()

    def get_graph(self) -> GraphInterface:
        """
        :return: the directed graph on which the algorithm works on.
        """
        return self.graph

    def load_from_json(self, file_name: str) -> bool:
        """
        Loads a graph from a json file.
        @param file_name: The path to the json file
        @returns True if the loading was successful, False o.w.
        """

        new_graph = DiGraph()

        try:

            with open(file_name, 'r') as file:
                data = json.load(file)

                for n in data['Nodes']:

                    try:

                        pos = n['pos'].split(",")
                        x, y, z = float(pos[0]), float(pos[1]), float(pos[2])
                        new_graph.add_node(n['id'], (x, y, z))
                    except:
                        new_graph.add_node(n['id'])

                for e in data['Edges']:
                    new_graph.add_edge(e['src'], e['dest'], e['w'])

                self.graph = new_graph

                return True

        except OSError as e:
            print("OSError {}".format(e))

        except:
            return False

    def save_to_json(self, file_name: str) -> bool:
        """
        Saves the graph in JSON format to a file
        @param file_name: The path to the out file
        @return: True if the save was successful, False o.w.
        """
        nodes = self.graph.get_all_v()

        data = {'Edges': [], 'Nodes': []}

        for n in nodes:
            node = nodes[n]
            if node.pos:
                data['Nodes'].append({'id': node.key, 'pos': node.pos})
            else:
                data['Nodes'].append({'id': node.key})

            for o in node.out_edges:
                data['Edges'].append({
                    'src': node.key,
                    'dest': o,
                    'w': node.out_edges[o].weight
                })
        try:
            with open(file_name, 'w') as file:
                json.dump(data, file)
                return True

        except OSError as e:
            print("OSError {}".format(e))
            return False

    def shortest_path(self, id1: int, id2: int) -> (float, list):
        """
        Returns the shortest path from node id1 to node id2 using Dijkstra's Algorithm
        @param id1: The start node id
        @param id2: The end node id
        @return: The distance of the path, a list of the nodes ids that the path goes through

        Example:
#        >>> from GraphAlgo import GraphAlgo
#        >>> g_algo = GraphAlgo()
#        >>> g_algo.addNode(0)
#        >>> g_algo.addNode(1)
#        >>> g_algo.addNode(2)
#        >>> g_algo.addEdge(0,1,1)
#        >>> g_algo.addEdge(1,2,4)
#        >>> g_algo.shortestPath(0,1)
#        (1, [0, 1])
#        >>> g_algo.shortestPath(0,2)
#        (5, [0, 1, 2])

        Notes:
        If there is no path between id1 and id2, or one of them dose not exist the function returns (float('inf'),[])
        More info:
        https://en.wikipedia.org/wiki/Dijkstra's_algorithm
        """

        nodes = self.graph.get_all_v()

        if id1 in nodes and id2 in nodes:

            parent = {i: None for i in nodes}
            distance = {i: float('inf') for i in nodes}

            distance[id1] = 0
            visit = set()

            # this is a priority queue = (priority, node_id)
            priority_q = [(distance[id1], id1)]

            while priority_q:

                current_distance, index = heapq.heappop(priority_q)

                if index in visit:
                    continue

                visit.add(index)

                for ni in self.graph.all_out_edges_of_node(index):
                    if ni in visit:
                        continue

                    dist = current_distance + self.graph.all_out_edges_of_node(
                        index)[ni].weight

                    if dist < distance[ni]:
                        distance[ni] = dist
                        parent[ni] = index

                        heapq.heappush(priority_q, (dist, ni))

            runner = id2
            path = [runner]

            while runner:

                path.append(parent[runner])
                runner = parent[runner]

                if runner == id1:
                    break

                if runner is None:
                    return distance[id2], []

            # path[::-1] to reverse path
            return distance[id2], path[::-1]

        return float('inf'), []

    def transpose(self):
        """
        transpose a graph
        :return: a transposed graph
        """

        transposed = DiGraph()
        nodes = self.graph.get_all_v()

        for node in nodes:
            transposed.add_node(node)

        for node in nodes:
            for ni in nodes[node].out_edges:
                transposed.add_edge(ni, node, nodes[node].out_edges[ni].weight)

        return transposed

    def BFS_util(self, node, nodes, stack):
        q = [node]
        nodes[node].tag = True

        while q:
            index = q.pop()

            for i in self.graph.all_out_edges_of_node(index):
                if not nodes[i].tag:
                    nodes[i].tag = True
                    q.append(i)
            stack.append(index)

    def BFS(self, node, graph, final):
        """
        this method implement breath-first search
        :param node: the node
        :param graph: the graph we want to implement on
        :param final: this is the list that we will need in the end
        :return: finds the gcc of the graph using transpose graph and BFS twice
        """
        q = [node]
        final.append(node)
        graph.graph_nodes[node].tag = True

        while q:
            index = heapq.heappop(q)

            for i in graph.all_out_edges_of_node(index):

                if not graph.graph_nodes[i].tag:
                    graph.graph_nodes[i].tag = True
                    q.append(i)
                    final.append(i)

        return final

    def connected_component(self, id1: int) -> list:
        """
        Finds the Strongly Connected Component(SCC) that node id1 is a part of.
        @param id1: The node id
        @return: The list of nodes in the SCC
        """
        nodes = self.graph.get_all_v()

        for node in nodes:
            nodes[node].tag = False

        stack = []
        self.BFS_util(id1, nodes, stack)
        transpose_graph = self.transpose()
        sConnected_path = []
        self.BFS(id1, transpose_graph, sConnected_path)

        return list(set(stack).intersection(sConnected_path))

    def connected_components(self) -> List[list]:
        """
        BFS(src)
        transpose_graph
        BFS(src -> transpose)

        Finds all the Strongly Connected Component(SCC) in the graph.
        @return: The list all SCC
        """
        nodes = self.graph.get_all_v()
        for node in nodes:
            nodes[node].tag = False

        result = []
        for node in nodes:
            flag = False
            for i in result:
                if node in i:
                    flag = True

            if flag:
                continue

            for x in nodes:
                nodes[x].tag = False

            stack = []
            self.BFS_util(node, nodes, stack)

            transpose_graph = self.transpose()
            sConnected_path = []
            nodes = transpose_graph.get_all_v()

            self.BFS(node, transpose_graph, sConnected_path)

            result.append(list(set(stack).intersection(sConnected_path)))

        return result

    def plot_graph(self) -> None:
        """
        Plots the graph.
        If the nodes have a position, the nodes will be placed there.
        Otherwise, they will be placed in a random but elegant manner.
        @return: None
        """

        nodes = self.graph.get_all_v()
        for n in nodes:
            if nodes[n].pos is None:
                v_size = self.graph.v_size()
                x, y, z = random.uniform(0, v_size), random.uniform(0,
                                                                    v_size), 0
                nodes[n].pos = (x, y, z)
        X = []
        Y = []
        Z = []

        ax = plt.axes()

        for n in nodes:
            node = nodes[n]
            x, y, z = node.pos

            for out in node.out_edges:

                dest_node = self.graph.graph_nodes[out]

                dest_x, dest_y, dest_z = dest_node.pos

                ax.quiver(x,
                          y,
                          dest_x - x,
                          dest_y - y,
                          angles='xy',
                          scale_units="xy",
                          scale=1)

            X.append(x)
            Y.append(y)
            Z.append(z)

        plt.plot(X, Y, 'ro')
        plt.show()

    """
    isEquals for graphs
    """

    def __eq__(self, other=None):
        if other is not None:
            return self.get_graph() == other.get_graph()

        return False
Beispiel #24
0
 def __init__(self, graph: DiGraph = DiGraph()):
     self.graph = graph
     self.list = []
Beispiel #25
0
class GraphAlgo(GraphAlgoInterface):
    """
    * This class represents a set of graph theory algorithms to
    * apply on a directed, weighted graph data structure, including:
    * Saving and loading a graph, calculating shortest paths on the graph from
    * one node to another,
     checking if the graph is strongly connected, and so on...
    """

    def __init__(self, directed_graph: object = None): # TODO: should be change
        self._graph = DiGraph()
        if directed_graph is not None:
            if isinstance(directed_graph, DiGraph):
                self._graph = directed_graph

    def get_graph(self) -> GraphInterface:
        """
        @return: the directed graph on which the algorithm works on.
        """
        return self._graph

    def load_from_json(self, file_name: str) -> bool:
        """
        Loads a graph from a json file.
        @param file_name: The path to the json file
        @returns: True if the loading was successful, False o.w.
        """
        flag = True
        try:
            with open(file_name, 'r') as jsonFile:
                load = json.load(jsonFile)
                graphJson = DiGraph()
            for node in load["Nodes"]:
                if "pos" in node:
                    posJ = tuple(map(float, str(node["pos"]).split(",")))
                    graphJson.add_node(node_id=node["id"], pos=posJ)
                else:
                    graphJson.add_node(node_id=node["id"])
            for edge in load["Edges"]:
                graphJson.add_edge(id1=edge["src"], id2=edge["dest"], weight=edge["w"])
            self._graph = graphJson
            # print("load successes")
        except Exception as e:
            print(e)
            print("load failed")
            flag = False
        finally:
            return flag

    def save_to_json(self, file_name: str) -> bool:
        """
        Saves the graph in JSON format to a file
        @param file_name: The path to the out file
        @return: True if the save was successful, False o.w.
        """
        flag = True
        with open(file_name, "w") as jsonFile:
            try:
                d = {"Edges": [], "Nodes": []}
                for src in self._graph.out_edges.keys():
                    for dst, w in self._graph.all_out_edges_of_node(src).items():
                        d["Edges"].append({"src": src, "w": w.weight, "dest": dst})
                for key, value in self._graph.nodes.items():
                    if value.location is None:
                        d["Nodes"].append({"id": key})
                    else:
                        d["Nodes"].append({"pos": str(value.location), "id": key})
                s = d.__str__()
                s = s.replace(" ", "")
                s = s.replace("'", "\"")
                jsonFile.write(s)
                # print("Save Json was succeeded ")
            except Exception as e:
                print("Save Json was failed ")
                print(e)
                flag = False
            finally:
                return flag

    def shortest_path(self, id1: int, id2: int) -> (float, list):
        """
        * returns the the shortest path between src to dest - as an ordered List of nodes:
        * src--> n1-->n2-->...dest
        * Logic only was taken from: https://en.wikipedia.org/wiki/Shortest_path_problem
        * Note if no such path --> returns null;
        @Runtime: Regular BFS using a priority queue = O(|V|+|E|).
        @param id1  - start node
        @param id2 - end (target) node
        @return - the path between src and dest if there is one.
        """

        # Edge cases
        # Either one of the nodes does not exist in the graph.
        if id1 not in self._graph.get_all_v() or id2 not in self._graph.get_all_v():
            return float('inf'), []
        if id1 == id2:  # The path from a node to itself is empty and the total distance is 0
            return 0, []

        # Initialization
        src = id1
        dest = id2

        self.reset_tags()
        self.set_weights_infinity()

        prev_node = dict()  # A map that stores: {key(int): caller(Node)} (Which node called which)
        pq = Queue()  # A queue to prioritize nodes with lower weight
        visited = dict()  # Keep track of visited nodes

        total_dist = 0.0
        destination_found = False
        curr = self._graph.get_node(src)
        curr.weight = total_dist
        visited[curr.key] = True

        pq.put(curr)

        # Traverse
        while not pq.empty():
            curr = pq.get()  # Pop the next node with the lowest weight O(log(n))
            neighbors = self._graph.all_out_edges_of_node(curr.key)  # Neighbors of curr node
            for i in neighbors:  # Iterate over neighbors of curr
                out_edge = neighbors[i]  # out_edge: EdgeData
                neighbor = self._graph.get_node(out_edge.dest)  # neighbor: NodeData
                if not visited.get(neighbor.key):  # Process node if not visited
                    total_dist = curr.weight + out_edge.weight
                    if total_dist < neighbor.weight:
                        neighbor.weight = total_dist
                        prev_node.__setitem__(neighbor.key, curr)
                    if neighbor not in pq.queue:  # If not already in the queue, enqueue neighbor.
                        pq.put(neighbor)
            # Finished processing curr's neighbors
            if curr.key == dest:
                destination_found = True
            visited[curr.key] = True

        if destination_found:
            path = self.rebuild_path(prev_node, src, dest)  # A list of nodes that represents the path between id1->id2

            total_dist = path[len(path) - 1].weight
            return total_dist, path

        return float('inf'), []

    def rebuild_path(self, node_map: dict = None, src: int = 0, dest: int = 0) -> list:
        """
        * This method back-tracks, takes a map of int keys and NodeData values
        * inserts all nodes in the path to a list and return the list
        """
        if node_map is None or src == dest:
            return None
        ans = [self._graph.get_node(dest)]
        next_node = node_map.get(dest)
        ans.append(next_node)
        while next_node.key is not src:  # Backtrack from dest to src
            ans.append(node_map.get(next_node.key))
            next_node = node_map.get(next_node.key)
        if self._graph.get_node(src) not in ans:
            ans.append(self._graph.get_node(src))

        ans.reverse()  # Inserted from
        return ans

    def reset_tags(self):
        for key in self._graph.get_all_v().keys():
            node = self.get_graph().get_node(key)
            node.tag = 0

    def set_weights_infinity(self):
        for key in self._graph.get_all_v().keys():
            node = self._graph.get_node(key)
            node.weight = float('inf')

    def connected_component(self, id1: int) -> list:
        """
        * Finds the Strongly Connected Component(SCC) that node id1 is a part of.
        * Notes: If the graph is None or id1 is not in the graph, the function should return an empty list []
        @param id1: The node id
        @return: The list of nodes in the SCC
        """
        if self._graph is None or self._graph.get_node(id1) is None:
            return []

        self.reset_tags()  # This method executes a BFS and tag nodes so reset_tags() must be called.

        # Traverse the original graph, from node id1, and tag all reachable nodes
        ans = []
        src = id1  # alias
        original_graph = self.get_graph()
        self.traverse_breadth_first(src, original_graph)
        # Transpose/Reverse graph's edges
        transposed_graph = self.reverse_graph()
        # Traverse the transposed graph, from node id1, and un-tag all reachable nodes
        self.traverse_breadth_first(src, transposed_graph)

        # Iterate over nodes in the transposed graph and find the nodes that are tagged twice!
        for key in transposed_graph.get_all_v():
            node = transposed_graph.get_node(key)
            if node.tag == 2:
                ans.append(self._graph.get_node(node.key))  # Append original node
        return ans

    def traverse_breadth_first(self, src: int = 0, graph: GraphInterface = None):
        """
        * This method is made to traverse any node in the graph and set tag on them using bfs algorithm.
        """
        if not isinstance(graph, DiGraph) or graph is None or self._graph.get_node(src) is None:
            return
        curr = graph.get_node(src)

        q = Queue()

        q.put(curr)
        curr.tag += 1

        while not q.empty():

            curr = q.get()
            out_edges = graph.all_out_edges_of_node(curr.key)

            for i in out_edges:
                out_edge = out_edges[i]
                neighbor = graph.get_node(out_edge.dest)  # Get curr's neighbor
                if neighbor.tag == curr.tag - 1:
                    neighbor.tag += 1  # If un-tagged -> tag it.
                    q.put(neighbor)  # and enqueue it

    def reverse_graph(self) -> GraphInterface:
        """
        * This method transposes the given graph.
        * The new graph will have the same set of vertices V = {v1, v2, .. , v(n)},
        * And all transposed edges. E = {(v1,v2), (v2,v6), .. }, E(transposed) = {(v2,v1), (v6,v2), ..}.
        * @param g - the given graph.
        * @return a transposed directed_weighted_graph.
        """
        ans = DiGraph()

        nodes = self._graph.get_all_v()  # {key: NodeData}
        for key in nodes:
            ans.add_node(key)
            ans.get_node(key).tag = self._graph.get_node(key).tag

        for key in nodes:
            out_edges = self._graph.all_out_edges_of_node(key)
            for edge in out_edges:
                e = out_edges.get(edge)
                ans.add_edge(e.dest, e.src, e.weight)

        return ans

    def connected_components(self) -> List[list]:
        """
        * This method finds all the Strongly Connected Components(SCC) in the graph.
        * Notes: If the graph is None the function should return an empty list []
        @return: The list all SCC
        """
        self.reset_tags()
        ans = []
        visited = dict()  # A dictionary of visited nodes

        for key in self._graph.get_all_v():
            if not visited.get(key):
                path = self.connected_component(key)
                for node in path:
                    visited.__setitem__(node.key, True)
                ans.append(path)
        return ans

    def plot_graph(self):
        """
        Plots the graph.
        If the nodes have a position, the nodes will be placed there.
        Otherwise, they will be placed in a random but elegant manner using get_random_location() function.
        """
        g = self.get_graph()
        plt.title("Our graph:" + g.__str__())
        plt.xlabel("X")
        plt.ylabel("-<")  # I should flip 'Y' letter so I decided to write it by a tricky way. :)
        for src, node in g.get_all_v().items():
            # Print the node point
            if node.location is None:
                pos = self.get_random_location()  # get a elegant location
                node.location = GeoLocation(pos)
            plt.plot(node.location.x, node.location.y, marker='o', markerfacecolor='red', markersize=3, color='yellow')
            plt.text(node.location.x, node.location.y, str(node.key))
            # Print the edge line
            for dest in g.all_out_edges_of_node(src).keys():
                x1 = g.get_all_v()[src].location.x
                y1 = g.get_all_v()[src].location.y
                if g.get_all_v()[dest].location is None:
                    pos = self.get_random_location()
                    g.get_all_v()[dest].location = GeoLocation(pos)
                    g.get_all_v()[dest].location = GeoLocation(pos)
                x2 = g.get_all_v()[dest].location.x
                y2 = g.get_all_v()[dest].location.y
                plt.arrow(x1, y1, x2 - x1, y2 - y1, width=0.00001, linewidth=0.05)
        plt.show()

    def get_random_location(self):
        """
        * This method was made to return a random location for a node when then node doesn't have any location.
        * How it work?
        * We get the max and min of the bounding box and then we set the nodes location on a random range inside it.
        * if there is no bounding box , which means there is no node location enough to set this bounding box,
        * so we set the nodes location in a range of x=[32,33],y=[35,36],z=0.
        """
        max_x, max_y, max_z, min_x, min_y, min_z = self.get_max_and_min()
        if max_x == float('-inf') and min_x == float('inf') and max_y == float('-inf') and min_y == float('inf') and \
                max_z == float('-inf') and min_z == float('inf'):
            x = random.uniform(32, 33)
            y = random.uniform(35, 36)
            z = 0
            ans = x, y, z
            return ans
        counter = 0
        for src, node in self._graph.get_all_v().items():
            if node.location is not None:
                counter += 1
        x = random.uniform(max_x, min_x)
        y = random.uniform(max_y, min_y)
        z = random.uniform(max_z, min_z)
        if counter == 0:  # means all nodes doesn't have any location
            x = random.uniform(32, 33)
            y = random.uniform(35, 36)
            z = 0
            ans = x, y, z
        else:
            ans = x, y, z
        return ans

    def get_max_and_min(self):
        """
        This method get the max and min of the bounding box on current graph.
        @return max and min of bounding box , o.w -inf&inf
        """
        max_x = float('-inf')
        min_x = float('inf')
        max_y = float('-inf')
        min_y = float('inf')
        max_z = float('-inf')
        min_z = float('inf')
        ans = max_x, max_y, max_z, min_x, min_y, min_z
        counter = 0
        for src, node in self._graph.get_all_v().items():
            if node.location is not None:
                x = node.location.x
                y = node.location.y
                z = node.location.z
                counter += 1
                max_x = x if x > max_x else max_x
                min_x = x if x < min_x else min_x
                max_y = y if y > max_y else max_y
                min_y = y if y < min_y else min_y
                max_z = z if z > max_z else max_z
                min_z = z if z < min_z else min_z
        if counter > 4:
            ans = max_x, max_y, max_z, min_x, min_y, min_z
        return ans

    def __repr__(self):
        return self._graph.__repr__()

    def __str__(self):
        return self._graph.__str__()
Beispiel #26
0
def createGraph():
    g = DiGraph()
    g.add_node(0, (32, 30, 6))
    g.add_node(1, (22, 43))
    g.add_node(2, (50, 50))
    g.add_node(3, (52, 13))
    g.add_node(4, (52, 33))
    g.add_node(5, (12, 33))
    g.add_node(6, (1, 2))
    g.add_edge(0, 1, 0.5)
    g.add_edge(5, 1, 0.5)
    g.add_edge(1, 0, 0.5)
    g.add_edge(5, 4, 0.5)
    g.add_edge(2, 1, 1.5)
    g.add_edge(2, 3, 2.5)
    g.add_edge(4, 1, 1.5)
    g.add_edge(5, 3, 3.5)
    g.add_edge(3, 5, 4)
    g.add_edge(4, 2, 6)
    g.add_edge(1, 2, 0.5)

    return g
Beispiel #27
0
 def __init__(self, directed_graph: object = None): # TODO: should be change
     self._graph = DiGraph()
     if directed_graph is not None:
         if isinstance(directed_graph, DiGraph):
             self._graph = directed_graph
Beispiel #28
0
class GraphAlgo(GraphAlgoInterface):
    """This class represents the directed weighted graph's algorithms"""

    visited, unvisited, finish = 1, -1, 0

    def __init__(self, graph: DiGraph = None):
        if graph:
            self.graph = graph
        else:
            self.graph = DiGraph()

    def get_graph(self) -> GraphInterface:
        """
        @return: the directed graph on which the algorithm works on.
        """
        return self.graph

    def init_json(self, new_graph: dict) -> None:
        """
        Initializes a new graph from Json format.
        @param: dict - a new graph
        """
        nodes = new_graph.get("Nodes")
        edges = new_graph.get("Edges")
        for node in nodes:
            k = node.get("id")
            x, y = uniform(0.0, 100), uniform(0.0, 100)
            if node.get("pos"):
                tmp = node.get("pos").split(",")
                x, y = float(tmp[0]), float(tmp[1])
            self.graph.add_node(node_id=k, pos=(x, y))
        for edge in edges:
            self.graph.add_edge(id1=edge.get("src"),
                                id2=edge.get("dest"),
                                weight=edge.get("w"))

    def init_my_graph_from_json(self, new_graph) -> None:
        """
        Initializes our graph from Json file.
        @return: None
        """
        g = new_graph.get("graph")
        for k, v in g.items():
            x, y = uniform(0.0, 100), uniform(0.0, 100)
            if v.get("pos"):
                tmp = v.get("pos")
                x, y = float(tmp[0]), float(tmp[1])
            self.graph.add_node(node_id=int(k), pos=(x, y))
        for k, v in g.items():
            tmp = v.get("outside")
            for ni, w in tmp.items():
                self.graph.add_edge(id1=int(k), id2=int(ni), weight=w)

    def load_from_json(self, file_name: str) -> bool:
        """
        Loads a graph from a json file.
        @param file_name: The path to the json file
        @returns True if the loading was successful, False o.w.
        """
        self.__init__()
        try:
            with open(file_name, "r") as f:
                new_graph = json.load(f)
        except IOError:
            return False
        if new_graph.get("graph"):
            self.init_my_graph_from_json(new_graph)
        else:
            self.init_json(new_graph)

        return True

    def encoder(self, o):
        """
        Returns a dictionary
        """
        return o.as_dict()

    def save_to_json(self, file_name: str) -> bool:
        """
        Saves the graph in JSON format to a file
        @param file_name: The path to the out file
        @return: True if the save was successful, False o.w.
        """
        try:
            with open(file_name, "w") as f:
                json.dump(self.graph, default=self.encoder, indent=4, fp=f)
        except IOError:
            return False
        return True

    def init_algorithm_graph(self):
        """
        Initializes the GraphAlgo
        """
        for i in self.graph.get_all_v():
            node = self.graph.get_node(i)
            node.set_value(float('inf'))
            node.set_tag(self.unvisited)
            node.set_prev(None)

    def seek_path(self, id2: int, value=float) -> (float, list):
        """
        Returns the shortest path distance (as weight) and its route of vertices as a list
        @param: int - id1
        @param: int - id2
        @param: float - value
        @return: (float, list)
        """
        prev = self.graph.get_node(id2)
        path = [prev.get_key()]

        while prev.get_prev() is not None:
            prev = prev.get_prev()
            path.append(prev.get_key())
        path.reverse()
        return value, path

    def shortest_path(self, id1: int, id2: int) -> (float, list):
        """
        Returns the shortest path from node id1 to node id2 using Dijkstra's Algorithm.
        Adds to every vertex, the previous vertex which the vertex "came from",
        finally, sends to help function seek_path() which returns a Tuple of the distance
        and the list of the vertices creating the route.
        @param id1: The start node id
        @param id2: The end node id
        @return: The distance of the path, a list of the vertices IDs which the path goes through
                 If no such path, or one of them doesn't exist the function returns (float('inf'),[])
        """
        if self.graph.get_node(id1) is None or self.graph.get_node(
                id2) is None:
            return float('inf'), []
        if id1 is id2:
            return 0.0, [self.graph.get_node(id1)]

        self.init_algorithm_graph()
        self.graph.get_node(id1).set_value(0)
        queue = PriorityQueue()
        queue.put(self.graph.get_node(id1))
        while not queue.empty():
            curr_node = queue.get(0)
            if curr_node.get_key() == id2 or curr_node.get_value() == float(
                    'inf'):
                if curr_node.get_value is float('inf'):
                    return float('inf'), []
                return self.seek_path(id2, curr_node.get_value())
            for i, w in curr_node.get_outside().items():
                weight = curr_node.get_value() + w
                ni = self.graph.get_node(i)
                if ni.get_tag() is self.unvisited and ni.get_value() > weight:
                    ni.set_value(weight)
                    queue.put(ni)
                    # sorted(queue.queue)
                    ni.set_prev(curr_node)
            curr_node.set_tag(self.visited)
        return float('inf'), []

    def connected_component(self, id1: int, reset: int = 0) -> list:
        """
        Finds the Strongly Connected Component (SCC) which node id1 is a part of.
        Help function sub_graph returns the reversed graph of all the vertices we can get to from
        vertex id1 and the list of all these vertices using DFS algorithm.
        Then, help function direction() returns the list of all vertices which we got
        to from the reverse graph using DFS algorithm.
        Finally, help function union(), unites both lists nd returns the Strongly Connected Component (SCC)
        @param id1: The vertex id
        @param reset: int
        @return: The list of nodes in the SCC
                 If the graph is None or id1 is not in the graph, the function returns an empty list []
        """

        if self.graph.get_node(id1) is None:
            return []
        if self.graph.v_size() == 1:
            return [id1]
        if reset == 0:
            self.init_algorithm_graph()

        reverse_graph, straight_list = self.sub_graph(id1)
        # straight_list = self.direction(id1, self.graph)
        reverse_list = self.direction(id1, reverse_graph)

        return self.union(straight_list, reverse_list)

    def direction(self, id1: int, g: DiGraph) -> list:
        """
        Returns the list of the neighbors of a vertex
        @param: int: id1
        @param: DiGraph - a graph
        @return: list
        """
        d = deque()
        d.append(id1)
        li = [id1]
        while len(d) != 0:
            curr_node = d.popleft()
            for ni in g.all_out_edges_of_node(curr_node).keys():
                if g.get_node(ni).get_tag() is self.unvisited:
                    d.append(ni)
                    g.get_node(ni).set_tag(self.visited)
                    li.append(ni)
        return li

    def union(self, straight: list, reverse: list) -> list:
        """
        Returns a union of both lists straight & reverse as another list
        @return: list
        """
        return list(set(straight) & set(reverse))

    def sub_graph(self, id1: int) -> Tuple[DiGraph, List]:
        """
        Returns the graph reverse and the list of neighbors which you can get to, from a vertex
        @param: int - id1
        @return: Tuple[DiGraph, List]
        """
        d = deque()
        d.append(id1)
        reverse_graph = DiGraph()
        reverse_graph.add_node(id1)
        li = [id1]
        while len(d) != 0:
            curr_node = d.popleft()
            for ni in self.graph.all_out_edges_of_node(curr_node).keys():
                reverse_graph.add_node(ni)
                w = self.graph.get_node(curr_node).get_outside().get(ni)
                reverse_graph.add_edge(ni, curr_node, w)
                if self.graph.get_node(ni).get_tag() is self.unvisited:
                    d.append(ni)
                    self.graph.get_node(ni).set_tag(self.visited)
                    li.append(ni)

        return reverse_graph, li

    def connected_components(self) -> List[list]:
        """
        Finds all the Strongly Connected Components (SCC) in the graph.
        Passes through every vertex and sends it to the function connected_component()
        as long as the vertex is unvisited yet.
        @return: The list of all SCC
                 If the graph is None, the function returns an empty list []
        """
        if self.graph is None:
            return []
        scc = []
        s = set()
        for i, node in enumerate(self.graph.get_all_v().keys()):
            if node not in s:
                connected = self.connected_component(node, i)
                s.update(connected)
                scc.append(connected)

        return scc

    def set_pos_for_all(self):
        """
        Sets the position for every vertex in the graph randomly
        """
        for node in self.graph.get_all_v().keys():
            if self.graph.get_node(node).get_pos() is None:
                self.graph.get_node(node).set_pos(
                    (uniform(0.0, 100), uniform(0.0, 100)))
            else:
                break

    def plot_graph(self) -> None:
        """
        Plots the graph.
        If the vertices have a position, the vertices will be placed there.
        Otherwise, they will be placed in a random but elegant manner.
        @return: None
        """
        fig, ax = plt.subplots()
        self.init_algorithm_graph()
        self.set_pos_for_all()
        li_x, li_y, n = [], [], []
        for node in self.graph.get_all_v().keys():
            xyA = self.graph.get_node(node).get_pos()
            x, y = self.graph.get_node(node).get_pos()
            li_x.append(x)
            li_y.append(y)
            n.append(node)
            for ni in self.graph.all_out_edges_of_node(node):
                xyB = self.graph.get_node(ni).get_pos()
                con = ConnectionPatch(xyA,
                                      xyB,
                                      "data",
                                      "data",
                                      arrowstyle="-|>",
                                      shrinkA=5,
                                      shrinkB=5,
                                      mutation_scale=13,
                                      fc="r")
                ax.plot([xyA[0], xyB[0]], [xyA[1], xyB[1]], "o")
                ax.add_artist(con)

        ax.scatter(li_x, li_y)
        for i, txt in enumerate(n):
            ax.annotate(n[i], (li_x[i], li_y[i]))
        plt.xlabel("x coordinates")
        plt.xlabel("x coordinates")
        plt.ylabel("y coordinates")
        plt.title("My Graph")
        plt.show()

    def __str__(self) -> str:
        """
        @return: Returns the graph as a String.
        """
        return self.graph.__str__()

    def __repr__(self) -> str:
        """
        @return: Returns the graph as a String.
        """
        return self.graph.__str__()

    def __eq__(self, other) -> bool:
        """
        Checks if two GraphAlgo are equals.
        @return: bool
        """
        if isinstance(other, GraphAlgo):
            return self.graph.__eq__(other.graph)
        elif isinstance(other, GraphInterface):
            return self.graph.__eq__(other)
        return False
Beispiel #29
0
class GraphAlgo(GraphAlgoInterface):
    def __init__(self, g: tuple = None):
        if g is None:
            self.g = DiGraph()
        else:
            self.g = g

    def get_graph(self) -> GraphInterface:
        return self.g

    def load_from_json(self, file_name: str):
        self.g = DiGraph()
        with open(file_name) as f:
            data = json.load(f)
            for n in data['Nodes']:
                self.g.add_node(n['id'])
            for e in data['Edges']:
                self.g.add_edge(e['src'], e['dest'], e['w'])

    def save_to_json(self, file_name: str):
        data = {"Edges": [], "Nodes": []}
        for n in self.g.get_all_v().keys():
            data["Nodes"].append({"id": n})
        for n in self.g.get_all_v().keys():
            for n2, w in self.g.all_out_edges_of_node(n).items():
                data["Edges"].append({"src": n, "w": w, "dest": n2})
        with open(file_name, 'w+') as f:
            json.dump(data, f)

    def shortest_path(self, id1: int, id2: int) -> (float, list):
        distancedict = {}
        q = []
        list = []
        q.append(id1)
        distancedict.update({id1: 0})
        while len(q) != 0:
            tmp = q.pop(0)
            for i in self.g.all_out_edges_of_node(tmp):
                if i not in distancedict:
                    q.append(i)
                    distancedict.update({
                        i:
                        self.g.all_out_edges_of_node(tmp)[i] +
                        distancedict[tmp]
                    })
                elif (self.g.all_out_edges_of_node(tmp)[i] +
                      distancedict[tmp]) < distancedict[i]:
                    q.append(i)
                    distancedict.update({
                        i:
                        self.g.all_out_edges_of_node(tmp)[i] +
                        distancedict[tmp]
                    })
        if not id2 in distancedict:
            return math.inf, []
        tmp2 = id2

        while tmp2 != id1:
            for i in self.g.all_in_edges_of_node(tmp2):
                distancedict[i]
                if (self.g.all_out_edges_of_node(i)[tmp2] +
                        distancedict[i] == distancedict[tmp2]):
                    list.append(i)
                    tmp2 = i
                    break
        list.reverse()
        return (distancedict[id2], list)

    def SCCUtil(self, u):
        next = 0
        nextgroup = 0
        index = [None] * self.g.v_size()
        lowlink = [None] * self.g.v_size()
        onstack = [False] * self.g.v_size()
        stack = []
        groups = []
        groupid = {}
        work = [(u, 0)]
        while work:
            v, i = work[-1]
            del work[-1]
            if i == 0:
                index[v] = next
                lowlink[v] = next
                next += 1
                stack.append(v)
                onstack[v] = True
            recurse = False
            for j in self.g.all_out_edges_of_node(v).keys():
                w = j
                if index[w] == None:
                    work.append((v, j + 1))
                    work.append((w, 0))
                    recurse = True
                    break
                elif onstack[w]:
                    lowlink[v] = min(lowlink[v], index[w])
            if recurse: continue
            if index[v] == lowlink[v]:
                com = []
                while True:
                    w = stack[-1]
                    del stack[-1]
                    onstack[w] = False
                    com.append(w)
                    groupid[w] = nextgroup
                    if w == v: break
                groups.append(com)
                nextgroup += 1
            if work:
                w = v
                v, _ = work[-1]
                lowlink[v] = min(lowlink[v], lowlink[w])
        return groups

    def connected_component(self, id1: int):
        for i in self.SCCUtil(id1):
            for j in i:
                if (j == id1):
                    return i
        return list()

    def Diff(self, li1, li2):
        return (list(list(set(li1) - set(li2)) + list(set(li2) - set(li1))))

    def connected_components(self):
        check = list(self.g.get_all_v().keys())
        ans = list()
        for i in self.g.get_all_v().keys():
            obj = self.SCCUtil(i)
            for j in obj:
                if check.__contains__(j[0]):
                    j.reverse()
                    ans.append(j)
                    check = self.Diff(check, j)

        if not self.g.get_all_v().keys():
            ans.append(list())
        return ans

    def plot_graph(self):
        for key, val in self.g.get_all_v().items():
            for node, _ in self.g.all_out_edges_of_node(key).items():
                x2, y2 = self.g.get_all_v()[node]
                x1, y1 = val
                plt.plot([x1, x2], [y1, y2], marker='o')
        plt.show()
Beispiel #30
0
def check0():
    """
    This function tests the naming (main methods of the DiGraph class, as defined in GraphInterface.
    :return:
    """
    g = DiGraph()  # creates an empty directed graph
    for n in range(4):
        g.add_node(n)
    g.add_edge(0, 1, 1)
    g.add_edge(1, 0, 1.1)
    g.add_edge(1, 2, 1.3)
    g.add_edge(2, 3, 1.1)
    g.add_edge(1, 3, 1.9)
    g.remove_edge(1, 3)
    g.add_edge(1, 3, 10)
    print(g)  # prints the __repr__ (func output)
    print(g.get_all_v())  # prints a dict with all the graph's vertices.
    print(g.all_in_edges_of_node(1))
    print(g.all_out_edges_of_node(1))
Beispiel #31
0
 def __init__(self, g: tuple = None):
     if g is None:
         self.g = DiGraph()
     else:
         self.g = g