コード例 #1
0
    def test_add_remove(self):
        """
        This test contains the basic methods implemented in DiGraph
        @:param: mc -> measured by the method "get_mc"
        @:param: nodes -> measured by the method "v_size"
        @:param: edges -> measured by the method "e_size"
        :return: True if it  works
        """
        print("test add remove")
        graph = DiGraph()
        graph.add_node(1)
        self.assertEqual(graph.v_size(), 1,
                         "You have added a node to your Graph")
        self.assertEqual(graph.get_mc(), 1,
                         "You have added a node to your Graph")
        self.assertEqual(graph.e_size(), 0, "The graph is Edges free")
        graph.add_node(2)
        self.assertEqual(graph.v_size(), 2,
                         "You have added a node to your Graph")

        graph.add_edge(1, 2, 2.0)
        self.assertEqual(graph.get_mc(), 3, "You have a 2 node Graph")
        self.assertEqual(graph.e_size(), 1, "The graph has 1 Edge")
        graph.remove_node(2)
        self.assertFalse(2 in graph.get_all_v())
        print("Completed")
コード例 #2
0
    def test_shortest_path_list(self):
        """Creating a graph and checking the shortestPathDist on it"""

        print("Checking shortest_path_list... ")
        g1 = DiGraph()
        for i in range(0, 5):
            g1.add_node(i)
        g1.add_edge(0, 1, 2.5)
        g1.add_edge(0, 1, 20)
        g1.add_edge(1, 2, 6.7)
        g1.add_edge(2, 3, 40)
        g1.add_edge(3, 4, 10)
        g1.add_edge(4, 3, 1)
        g1.add_edge(3, 2, 16)
        g1.add_edge(2, 1, 0.5)
        g1.add_edge(1, 0, 20)
        g1.add_edge(4, 2, 70)
        g1.add_edge(2, 4, 70)
        g1.add_edge(1, 2, 100)
        g1.add_edge(3, 1, 80)
        g1.add_edge(1, 3, 80)

        operator = GraphAlgo(g1)
        self.assertEqual([1], operator.shortest_path(1, 1)[1])
        self.assertEqual([0, 1, 2], operator.shortest_path(0, 2)[1])
        self.assertEqual([0, 1, 2, 3], operator.shortest_path(0, 3)[1])
        self.assertEqual([0, 1, 2, 3, 4], operator.shortest_path(0, 4)[1])
        self.assertEqual([4, 3], operator.shortest_path(4, 3)[1])
        self.assertEqual([4, 3, 2], operator.shortest_path(4, 2)[1])
        self.assertEqual([4, 3, 2, 1], operator.shortest_path(4, 1)[1])
        self.assertEqual([4, 3, 2, 1, 0], operator.shortest_path(4, 0)[1])
コード例 #3
0
    def test_shortest_path_dist(self):
        """Creating a graph and checking the shortestPath on it"""

        print("Checking shortest_path _dist... ")
        g1 = DiGraph()
        for i in range(0, 5):
            g1.add_node(i)
        g1.add_edge(0, 1, 2.5)
        g1.add_edge(0, 1, 20)
        g1.add_edge(1, 2, 6.7)
        g1.add_edge(2, 3, 40)
        g1.add_edge(3, 4, 10)
        g1.add_edge(4, 3, 1)
        g1.add_edge(3, 2, 16)
        g1.add_edge(2, 1, 0.5)
        g1.add_edge(1, 0, 20)
        g1.add_edge(4, 2, 70)
        g1.add_edge(2, 4, 70)
        g1.add_edge(1, 2, 100)
        g1.add_edge(3, 1, 80)
        g1.add_edge(1, 3, 80)

        operator = GraphAlgo(g1)
        self.assertEqual(float('inf'), operator.shortest_path(9, 1)[0])
        self.assertEqual(0, operator.shortest_path(1, 1)[0])
        self.assertEqual(9.2, operator.shortest_path(0, 2)[0])
        self.assertEqual(49.2, operator.shortest_path(0, 3)[0])
        self.assertEqual(59.2, operator.shortest_path(0, 4)[0])
        self.assertEqual(1, operator.shortest_path(4, 3)[0])
        self.assertEqual(17, operator.shortest_path(4, 2)[0])
        self.assertEqual(17.5, operator.shortest_path(4, 1)[0])
        self.assertEqual(37.5, operator.shortest_path(4, 0)[0])
コード例 #4
0
 def test_add_edge_and_remove_edge(self):
     g = DiGraph()
     # adding nodes to the graph
     for i in range(10):
         g.add_node(i)
     # creating edges
     for i in range(4):
         g.add_edge(i, i + 3, i * 5 / 2)
     my_dict = g.get_all_v()
     # checking if the edges successfully added in the node
     for i in range(4):
         current_node = my_dict[i]
         edge_dic = current_node.edges_towards
         self.assertEqual(edge_dic[i + 3], i * 5 / 2)
         # removing the edges
     for i in range(4):
         g.remove_edge(i, i + 3)
     my_dict = g.get_all_v()
     flag = True
     # checking if the edges were removed successfully
     for i in range(4):
         current_node = my_dict[i]
         empty_dict = {}
         if empty_dict != current_node.edges_towards:
             flag = False
             break
     self.assertEqual(True, flag)
コード例 #5
0
 def test_get_all_v(self):
     g = DiGraph()
     my_dict = {}
     for i in range(10000):
         g.add_node(i)
         current_node = g.get_all_v()[i]
         my_dict[i] = current_node
     self.assertEqual(my_dict, g.get_all_v())
コード例 #6
0
 def single_node_graph(self) -> DiGraph:
     """
     This method creates a new graph with only one node
     :return: DiGraph
     """
     graph = DiGraph()
     graph.add_node(100)
     return graph
コード例 #7
0
 def test_add_node(self):
     g = DiGraph()
     flag = True
     for i in range(10):
         g.add_node(i)
         if not flag:
             break
     self.assertEqual(flag, True)
コード例 #8
0
 def test_all_out_edges_of_node(self):
     g = DiGraph()
     my_dict = dict()
     for i in range(15):
         g.add_node(i)
     for i in range(9):
         g.add_edge(0, i + 2, i * 5)
         my_dict[i + 2] = i * 5
     self.assertEqual(my_dict, g.all_out_edges_of_node(0))
コード例 #9
0
 def test_v_size(self):
     counter = 0
     g = DiGraph()
     for i in range(10000):
         g.add_node(i)
         counter += 1
     for i in range(500):
         g.remove_node(i)
         counter -= 1
     self.assertEqual(counter, g.v_size())
コード例 #10
0
 def liniar_graph(self) -> DiGraph:
     """
     This method create a linear 10 node graph
     :return: DiGraph
     """
     graph = DiGraph()
     for i in range(0, 10):
         graph.add_node(i)
     for i in range(0, 10):
         graph.add_edge(i, i + 1, 1.0)
     return graph
コード例 #11
0
 def test_remove_node(self):
     g = DiGraph()
     flag = True
     g.add_node(11)
     for i in range(10):
         g.add_node(i)
         g.add_edge(i, 11, 1)
     for i in range(10):
         flag = g.remove_node(i)
         if not flag:
             break
     self.assertEqual(flag, True)
コード例 #12
0
 def connected_graph(self) -> DiGraph:
     """
     This method create a linear 10 node graph
     :return: DiGraph
     """
     graph = DiGraph()
     for i in range(0, 10):
         graph.add_node(i)
     for i in range(0, 10):
         for j in range(0,10):
             if i is not j:
                 graph.add_edge(i, j, 1.0)
     return graph
コード例 #13
0
 def test_e_size(self):
     counter = 0
     g = DiGraph()
     for i in range(10000):
         g.add_node(i)
     for i in range(500):
         g.add_edge(i, (i + 1) * 10, i * 10 / 5)
         counter += 1
     self.assertEqual(counter, g.e_size())
     for i in range(100):
         g.remove_edge(i, (i + 1) * 10)
         counter -= 1
     self.assertEqual(counter, g.e_size())
コード例 #14
0
    def testa_shortest_path(self):
        """
        This method tests the shortest path between 2 nodes
        :return: True if the graphs are the same
        """
        print("third test")
        graph = DiGraph()
        graph.add_node(0)
        graph.add_node(1)
        graph.add_edge(0, 1, 0.0)
        algo = GraphAlgo(graph)
        algo.save_to_json("../tests/third_algo_test.json")
        s_path_algo = algo.shortest_path(0, 1)

        g_algo = GraphAlgo()
        file = '../tests/third_algo_test.json'
        g_algo.load_from_json(file)

        s_path_g_algo = g_algo.shortest_path(0, 1)
        self.assertEqual(s_path_algo, s_path_g_algo)
        print("Completed")
コード例 #15
0
    def test_ccg_graph(self):
        """
        This test checks if the graph is strongly connected
        :return: True if it does
        """
        print("eighth test")
        graph = DiGraph()
        graph.add_node(100)
        algo = GraphAlgo()
        algo.graph = graph
        ans = algo.connected_components()
        self.assertEqual([[100]], ans)

        g_algo = GraphAlgo(self.connected_graph())
        res = g_algo.connected_components()
        exp = []
        for i in g_algo.get_graph().graph_nodes:
            exp.append(i)

        self.assertEqual([exp], res)

        print("Completed")
コード例 #16
0
def create_graph() -> DiGraph:
    """
    Creates a new graph
    :return: DiGraph
    """
    graph = DiGraph()

    # add 10 nodes to the graph
    for i in range(1, 11):
        graph.add_node(i)

    # connects = 10
    graph.add_edge(1, 2, 20)
    graph.add_edge(2, 3, 5)
    graph.add_edge(2, 4, 90)
    graph.add_edge(2, 6, 7)
    graph.add_edge(3, 7, 21)
    graph.add_edge(4, 2, 1)
    graph.add_edge(5, 10, 1)
    graph.add_edge(7, 9, 12)
    graph.add_edge(8, 9, 1)
    graph.add_edge(10, 3, 75)
    # mc = 20
    return graph
コード例 #17
0
    def setUp(self):
        """The method creates 3 graphs for this unittest. connected graph and the other one isn't connected and
        the last one is scc_graph. algo initialized on connected"""
        self.startTime = time.time()
        g = DiGraph()
        for n in range(15):
            g.add_node(n)
        g.add_edge(0, 9, 2.5)
        g.add_edge(1, 9, 6.7)
        g.add_edge(4, 9, 40)
        g.add_edge(3, 10, 7)
        g.add_edge(3, 13, 27)
        g.add_edge(3, 2, 4.5)
        g.add_edge(14, 5, 21)
        g.add_edge(14, 8, 41)
        g.add_edge(11, 2, 17)
        self.is_not_connected = g

        graph = DiGraph()
        for n in range(10):
            graph.add_node(n)
        graph.add_edge(0, 1, 12)
        graph.add_edge(1, 2, 67)
        graph.add_edge(2, 3, 43)
        graph.add_edge(3, 4, 17)
        graph.add_edge(4, 5, 27)
        graph.add_edge(5, 6, 4.5)
        graph.add_edge(6, 7, 21)
        graph.add_edge(7, 8, 41)
        graph.add_edge(8, 9, 11.23)
        graph.add_edge(9, 0, 13.765649959)
        self.connected = graph

        di_graph = DiGraph()
        for n in range(10):
            di_graph.add_node(n)
        di_graph.add_edge(0, 1, 13)
        di_graph.add_edge(1, 2, 32)
        di_graph.add_edge(1, 4, 11)
        di_graph.add_edge(2, 3, 12)
        di_graph.add_edge(2, 6, 35)
        di_graph.add_edge(3, 2, 7)
        di_graph.add_edge(3, 7, 21)
        di_graph.add_edge(4, 0, 10)
        di_graph.add_edge(4, 5, 47)
        di_graph.add_edge(5, 6, 31)
        di_graph.add_edge(5, 8, 91)
        di_graph.add_edge(6, 5, 0.2)
        di_graph.add_edge(7, 3, 29)
        di_graph.add_edge(7, 6, 61)
        di_graph.add_edge(8, 5, 22)
        di_graph.add_edge(9, 8, 75)
        self.scc_graph = di_graph

        self.algo = GraphAlgo(self.connected)
コード例 #18
0
class TestDiGraph(unittest.TestCase):
    def setUp(self) -> None:
        """
        build the central graph for all the tests
        """
        self.graph = DiGraph()

        self.graph.add_node(1)
        self.graph.add_node(2)
        self.graph.add_node(3)
        self.graph.add_node(4)
        self.graph.add_node(5)

        self.graph.add_edge(1, 2, 600)
        self.graph.add_edge(2, 3, 600)
        self.graph.add_edge(3, 1, 600)
        self.graph.add_edge(3, 4, 600)
        self.graph.add_edge(4, 5, 600)
        self.graph.add_edge(5, 4, 600)

    def test_v_size(self):
        self.assertEqual(5, self.graph.v_size())

    def test_e_size(self):
        self.assertEqual(6, self.graph.e_size())

    def test_get_mc(self):
        self.assertEqual(11, self.graph.get_mc())

    def test_removeEdge(self):
        self.graph.remove_edge(3, 1)
        self.graph.remove_edge(2, 3)
        self.graph.remove_edge(5, 4)
        self.assertEqual(3, self.graph.e_size())
        self.graph.add_edge(3, 1, 1.67)
        self.assertEqual(4, self.graph.e_size())

    def test_removeNode(self):
        self.graph.remove_node(1)
        self.graph.remove_node(3)
        self.graph.remove_node(1)
        self.assertEqual(3, self.graph.v_size())

    def test_add_node(self):
        self.graph.add_node(6)
        self.graph.add_node(7)
        self.graph.add_node(8)
        self.graph.add_node(8)
        self.assertEqual(8, self.graph.v_size())

    def test_all_in_edges_of_node(self):
        self.assertTrue(len(self.graph.all_in_edges_of_node(1)) == 1)
        self.assertTrue(len(self.graph.all_in_edges_of_node(2)) == 1)
        self.graph.add_node(6)
        self.graph.add_edge(6, 3, 5.5)
        self.assertTrue(len(self.graph.all_in_edges_of_node(3)) == 2)

    def test_all_out_edges_of_node(self):
        self.assertTrue(len(self.graph.all_out_edges_of_node(2)) == 1)
        self.assertTrue(len(self.graph.all_out_edges_of_node(4)) == 1)
        self.assertTrue(len(self.graph.all_out_edges_of_node(3)) == 2)
コード例 #19
0
class GraphAlgo(GraphAlgoInterface):
    """This abstract class represents an interface of a graph algotirhms class."""
    myGraph = DiGraph()
    tropologicalSort = []
    sccList = []

    def __init__(self, graph=myGraph):  # , graph: DiGraph):
        self.myGraph = DiGraph()
        self.myGraph = graph
        self.tropologicalSort = []  # have the list in tropoligical sort
        self.sccList = []
        """Init the graph on which this set of algorithms operates on."""

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

    def get_graph_in_class(self) -> DiGraph:
        return self.myGraph
        """
        :return: the directed graph on which the algorithm works on.
        """

    def load_from_json(self, file_name: str) -> bool:
        try:
            with open(file_name, "r") as file:
                data = json.load(file)
            self.__init__()
            self.myGraph = DiGraph()
            for n in data.get("Nodes"):
                self.myGraph.add_node(n.get("id"), n.get("pos"))
            for e in data.get("Edges"):
                self.myGraph.add_edge(e.get("src"), e.get("dest"), e.get("w"))
            return True
        except Exception as e:
            return False
        """
        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.
        """

    def save_to_json(self, file_name: str) -> bool:
        try:
            with open(file_name, "w") as file:
                json.dump(self.myGraph.as_dict(), indent=4, fp=file)
            return True
        except Exception as e:
            return False
        """
        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.
        """

    def shortest_path_list(self, src, dest):
        groupNodes = self.myGraph._nodes  # get and store the graphs' collection of vertices.
        parents = {
        }  # for (space I guess.. wanted to achieve time) complexity reason-
        # a hashmap of parents, to restore the parent of each node in the shortest path
        myListg = []  # create a list for returning the shortest path.
        #     groupNodes[i.getKey()] = i  # for every key in the nodes list put it in the hashmap with its key.

        desty = dest  # for further use

        if (dest == src):  # the path of node from itself to itself is itself.
            myListg.append(groupNodes.get(src))
            return myListg

        if src not in groupNodes.keys() or dest not in groupNodes.keys():
            return myListg

        groupNodesKeys = groupNodes.keys()
        for key in groupNodesKeys:  # initializing- prepearing the nodes for the proccess.
            (groupNodes.get(key)).setTagB(float('inf'))
            groupNodes.get(key).setInfo("")
            # Shortest distance- we set it to infinity cuz we didn't check it yet
            parents[
                key] = None  # None because initially, we don't have any path to reach

        (groupNodes.get(src)).setTagB(0)
        # distance a node to itelf is 0, and it's the distance of src from itself

        q = deque()  # deque
        groupNodesKeys = groupNodes.keys()
        for key in groupNodesKeys:
            # all nodes in the graph are unoptimized - thus are in Q
            # key is Integer
            q.append(groupNodes.get(key))

        minDist = float('inf')  # double
        minKey = -1  # int
        dist = 0  # double
        minNode = None  # node_data

        while q:  # while it's not empty
            for node in q:  # getting the smallest dist node
                # node is type NodeData
                if node.getTagB() <= minDist:
                    minDist = node.getTagB()
                    minKey = node.getKey()
                    minNode = node

            q.remove(minNode)
            # for every neighbor of minKey= neinode
            if (self.myGraph.all_out_edges_of_node(minKey)):
                for key in self.myGraph.all_out_edges_of_node(minKey).keys(
                ):  # where v has not yet been removed from Q.
                    # edge is type edge_data
                    # get node data from edge_data
                    neinode = self.myGraph._nodes.get(
                        key)  # (NodeData) #typed NodeData
                    # we're taking a node from myGraph, which is the neighbor of MINKEY node,
                    # which is the DEST in the edge between MINKEY and DEST.
                    if (
                            neinode in q
                    ):  # where v has not yet been removed from Q.#if it contains the neighbor node
                        dist = minDist + self.myGraph.all_out_edges_of_node(
                            minKey).get(key)
                        if (dist < neinode.getTagB()):
                            (neinode).setTagB(
                                dist
                            )  # it's the path's weight sum, why is it double? #(NodeData)
                            neinode.setInfo(str(minKey))

            minDist = float('inf')
        # done with while loop.
        desty = dest

        while (groupNodes.get(desty)
               ).getTagB() != 0 and groupNodes.get(desty).getInfo() != "":
            # typed NodeData
            # means you haven't yet reached the src because only the src has tag=0 (dist)
            # of 0 from itself.
            # myListg.append(groupNodes.get(desty))
            myListg.append(groupNodes.get(desty))
            desty = int(groupNodes.get(desty).getInfo())  # get the "father"
        # myListg.append(groupNodes.get(src)) <<list of nodes
        myListg.append(groupNodes.get(src))  # list of nodes id
        myListg.reverse(
        )  # reverse the list, 'cuz it came reversed, as we got from the dest to src by parents.
        if myListg[len(myListg) - 1].getKey() != dest:
            myListg.clear()

        return myListg
        """     returns the shortest path between src to dest - as an ordered List of nodes"""

    def shortest_path_dist(self, src, dest):
        if (src == dest):  # the distance from a node to itself is 0.
            return 0

        aj = self.shortest_path_list(src, dest)  # aj is type List
        if not (aj):
            return float(
                'inf')  # EMPTY aj MEANS THERE'S NO PATH FROM SRC TO DEST

        weight = (aj.pop(len(aj) - 1)).getTagB()
        return weight  # with -1 cuz of the edges num, if there are 2 nodes, theres only one edge connecting them
        """returns the length of the shortest path between src to dest"""

    def shortest_path(self, id1: int, id2: int) -> (float, list):
        nodesList = self.shortest_path_list(id1, id2)
        # print("nodes",nodesList)
        nodesKeys = []
        for node in nodesList:
            nodesKeys.append(node.key)
        # print("keys ",nodesKeys)
        dist = self.shortest_path_dist(id1, id2)
        return dist, nodesKeys

    """
    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
    """

    def connected_component(self, id1: int) -> list:
        if self.get_graph() is None or id1 not in self.get_graph().get_all_v(
        ).keys():
            return []
        return self.bfs(self.get_graph(), id1, {})
        """
        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
        """

    def connected_components(self) -> List[list]:
        if self.get_graph() is None or self.get_graph().v_size() == 0:
            return []
        all_the_SCC = []
        has_family = {}
        for key in self.get_graph().get_all_v():
            if key not in has_family.keys():
                all_the_SCC.append(self.bfs(self.get_graph(), key, has_family))
        return all_the_SCC
        """
        Finds all the Strongly Connected Component(SCC) in the graph.
        @return: The list all SCC
        """

    def bfs(self, g: DiGraph(), id1: int, has_family: dict) -> list:
        scc = []
        queue = deque()
        v_list = g.get_all_v()
        u = v_list.get(id1)
        scanned, scanned_reverse = {}, {}
        queue.append(u)
        scc.append(u.key)
        has_family[u.key] = True
        scanned[u] = True
        while queue:
            u = queue.popleft()
            if u.key in g._edges.keys():
                for key in g.all_out_edges_of_node(u.key).keys():
                    v = g.get_all_v().get(key)
                    if v not in scanned:
                        scanned[v] = True
                        queue.append(v)
        queue.append(g.get_all_v().get(id1))
        while queue:
            u = queue.popleft()
            if g.all_in_edges_of_node(u.key) is not None:
                for key in g.all_in_edges_of_node(u.key).keys():
                    v = g.get_all_v().get(key)
                    if v not in scanned_reverse:
                        scanned_reverse[v] = True
                        queue.append(v)
                        if v in scanned.keys() and key not in has_family.keys(
                        ):
                            scc.append(key)
                            has_family[key] = True
        return sorted(list(dict.fromkeys(scc)))

    """BFS algorithm: this method operates BFS twice: one for the original graph, and the
    second time for reverse graph. After this time we get SCC for specific node"""

    def plot_graph(self) -> None:
        fig, axes = plt.subplots(figsize=(8, 6))
        csfont = {'fontname': 'Comic Sans MS'}
        axes.set_title("Graph Plot", **csfont, fontsize=25)

        for node in self.myGraph._nodes.values():
            plt.scatter(node.pos[0], node.pos[1], s=25, color="red")
            plt.text(node.pos[0],
                     node.pos[1] + 0.0001,
                     str(node.key),
                     color="green",
                     fontsize=8)

        for src in self.myGraph._edges.keys():
            for dest in self.myGraph._edges.get(src).keys():
                x1 = self.myGraph._nodes.get(src).pos[0]
                y1 = self.myGraph._nodes.get(src).pos[1]
                x2 = self.myGraph._nodes.get(dest).pos[0]
                y2 = self.myGraph._nodes.get(dest).pos[1]

                if (self.myGraph._edges.get(dest)
                    ):  # if there's a chance for a two directional edge..
                    if (self.myGraph._edges.get(dest).get(src)
                        ):  # if there's a two- directional edge
                        srcNode = self.myGraph._nodes.get(src)
                        destNode = self.myGraph._nodes.get(dest)
                        plt.annotate("",
                                     xy=(destNode.pos[0], destNode.pos[1]),
                                     xytext=(srcNode.pos[0], srcNode.pos[1]),
                                     arrowprops=dict(arrowstyle='<->',
                                                     color='black'))
                    else:
                        # There's only a one directed edge
                        plt.annotate("",
                                     xy=(x2, y2),
                                     xytext=(x1, y1),
                                     arrowprops=dict(arrowstyle='->',
                                                     color='blue'))
                else:  # there's only a one directed edge
                    plt.annotate("",
                                 xy=(x2, y2),
                                 xytext=(x1, y1),
                                 arrowprops=dict(arrowstyle='->',
                                                 color='blue'))

        white_patch = mpatches.Patch(color='blue', label='(one)Directed edge')
        black_patch = mpatches.Patch(color='black', label='Bidirected edge')
        magenta_patch = mpatches.Patch(color='red', label='Node')
        plt.legend(handles=[black_patch, white_patch, magenta_patch])
        # axes.relim()
        # axes.autoscale_view()
        plt.show()
        """