예제 #1
0
    def is_neighbour_of(self, n1: AbstractNode, n2: AbstractNode) -> bool:
        """!
        \brief check if two nodes are neighbours
        We define the condition of neighborhood as having a common edge, not
        being the same

        \code{.py}

        >>> n1 = Node("n1", {})
        >>> n2 = Node("n2", {})
        >>> n3 = Node("n3", {})
        >>> n4 = Node("n4", {})
        >>> e1 = Edge(
        >>>     "e1", start_node=n1, end_node=n2, edge_type=EdgeType.UNDIRECTED
        >>> )
        >>> e2 = Edge(
        >>>     "e2", start_node=n2, end_node=n3, edge_type=EdgeType.UNDIRECTED
        >>> )
        >>> e3 = Edge(
        >>>     "e3", start_node=n3, end_node=n4, edge_type=EdgeType.UNDIRECTED
        >>> )
        >>> graph_2 = Graph(
        >>>   "g2",
        >>>   data={"my": "graph", "data": "is", "very": "awesome"},
        >>>   nodes=set([n1, n2, n3, n4]),
        >>>   edges=set([e1, e2, e3]),
        >>> )
        >>> graph_2.is_neighbour_of(n2, n3)
        >>> True
        >>> graph_2.is_neighbour_of(n2, n2)
        >>> False

        \endcode
        """

        def cond(
            n_1: AbstractNode, n_2: AbstractNode, e: AbstractEdge
        ) -> bool:
            """!
            \brief neighborhood condition
            """
            estart = e.start()
            eend = e.end()
            c1 = estart == n_1 and eend == n_2
            c2 = estart == n_2 and eend == n_1
            return c1 or c2

        gdata = BaseGraphOps.to_edgelist(self)

        n1_edge_ids = set(gdata[n1.id()])
        n2_edge_ids = set(gdata[n2.id()])
        edge_ids = n1_edge_ids.intersection(n2_edge_ids)
        # filter self loops
        edges = set([e for e in self.E if e.id() in edge_ids])
        return self.is_related_to(n1=n1, n2=n2, condition=cond, es=edges)
예제 #2
0
 def uniform_cost_search(
     g: AbstractGraph,
     start: AbstractNode,
     goal: AbstractNode,
     filter_fn: Callable[
         [Set[AbstractEdge], str], Set[AbstractEdge]
     ] = lambda es, n: set([e for e in es if e.start().id() == n]),
     costfn: Callable[[AbstractEdge, float], float] = lambda x, y: y + 1.0,
     is_min=True,
     problem_set: Optional[Set[AbstractEdge]] = None,
 ) -> Tuple[
     Dict[str, Union[int, AbstractNode, AbstractEdge, str]],
     Tuple[AbstractEdge],
 ]:
     """!
     Apply uniform cost search to given problem set
     """
     if not BaseGraphOps.is_in(g, start) or not BaseGraphOps.is_in(g, goal):
         raise ValueError("Start node or goal node is not in graph")
     problem_set = g.E if problem_set is None else problem_set
     pnode = {"cost": 0, "state": start.id(), "parent": None, "edge": None}
     frontier = PriorityQueue(is_min=is_min)
     frontier.insert(key=pnode["cost"], val=pnode)
     explored: Set[str] = set()
     while len(frontier) != 0:
         key, pn = frontier.pop()
         if pn["state"] == goal.id():
             return BaseGraphSearcher.from_ucs_result(pn), pn
         explored.add(pn["state"])
         for child_edge in filter_fn(problem_set, pn["state"]):
             child: AbstractNode = child_edge.get_other(pn["state"])
             cnode = {
                 "cost": costfn(child_edge, pn["cost"]),
                 "state": child.id(),
                 "parent": pn,
                 "edge": child_edge,
             }
             if (child.id() not in explored) or (
                 frontier.is_in(child, cmp_f=lambda x: x["state"]) is False
             ):
                 frontier.insert(cnode["cost"], cnode)
             elif frontier.is_in(child, cmp_f=lambda x: x["state"]) is True:
                 # node is already in frontier
                 ckey = frontier.key(child, f=lambda x: x["state"])
                 if ckey > cnode["cost"]:
                     frontier.insert(
                         cnode["cost"], cnode, f=lambda x: x["state"]
                     )
예제 #3
0
 def added_edge_between_if_none(
     g: AbstractGraph,
     n1: AbstractNode,
     n2: AbstractNode,
     is_directed: bool = False,
 ) -> BaseGraph:
     """!
     Add edge between nodes. If there are no edges in between.
     The flag is_directed specifies if the edge is directed or not
     """
     if not BaseGraphOps.is_in(g, n1) or not BaseGraphOps.is_in(g, n2):
         raise ValueError("one of the nodes is not present in graph")
     n1id = n1.id()
     n2id = n2.id()
     gdata = BaseGraphOps.to_edgelist(g)
     first_eset = set(gdata[n1id])
     second_eset = set(gdata[n2id])
     common_edge_ids = first_eset.intersection(second_eset)
     if len(common_edge_ids) == 0:
         # there are no edges between the nodes
         if isinstance(g, AbstractUndiGraph):
             edge = Edge.undirected(eid=str(uuid4()),
                                    start_node=n1,
                                    end_node=n2)
             return BaseGraphAlgOps.add(g, edge)
         elif isinstance(g, AbstractDiGraph):
             edge = Edge.directed(eid=str(uuid4()),
                                  start_node=n1,
                                  end_node=n2)
             return BaseGraphAlgOps.add(g, edge)
         elif is_directed is True:
             edge = Edge.directed(eid=str(uuid4()),
                                  start_node=n1,
                                  end_node=n2)
             return BaseGraphAlgOps.add(g, edge)
         elif is_directed is False:
             edge = Edge.undirected(eid=str(uuid4()),
                                    start_node=n1,
                                    end_node=n2)
             return BaseGraphAlgOps.add(g, edge)
         else:
             raise ValueError("Must specify an edge type to be added")
     else:
         # there is already an edge in between
         return g
예제 #4
0
    def edge_by_vertices(g: AbstractGraph, start: AbstractNode,
                         end: AbstractNode) -> Set[AbstractEdge]:
        """!
        \brief obtain edge set by using its vertices.

        We take all edges that consist of given two nodes

        \throws ValueError if any of argument nodes are not inside the graph.
        \throws ValueError if there are no edges that consist of argument nodes.
        """
        if not BaseGraphOps.is_in(g, start) or not BaseGraphOps.is_in(g, end):
            raise ValueError("one of the nodes is not present in graph")
        n1id = start.id()
        n2id = end.id()
        gdata = BaseGraphOps.to_edgelist(g)
        first_eset = set(gdata[n1id])
        second_eset = set(gdata[n2id])
        common_edge_ids = first_eset.intersection(second_eset)
        if len(common_edge_ids) == 0:
            raise ValueError("No common edges between given nodes")
        return set([e for e in g.E if e.id() in common_edge_ids])
예제 #5
0
    def outgoing_edges_of(g: AbstractGraph,
                          n: AbstractNode) -> FrozenSet[AbstractEdge]:
        """!
        \brief obtain the outgoing edge set of a given node.

        Outgoing edge set means all edges that start with the given node
        and end in another node. This information is mostly trivial for
        undirected graphs but becomes important for distinguishing
        parents from children in directed graphs.

        \param n node whose adjacent edges we are interested in

        \return edge set of node.

        \throw ValueError if node is not in graph we raise a value error
        \code{.py}

        >>> n1 = Node("n1", {})
        >>> n2 = Node("n2", {})
        >>> n3 = Node("n3", {})
        >>> n4 = Node("n4", {})
        >>> e1 = Edge(
        >>>     "e1", start_node=n1, end_node=n2, edge_type=EdgeType.UNDIRECTED
        >>> )
        >>> e2 = Edge(
        >>>     "e2", start_node=n2, end_node=n3, edge_type=EdgeType.UNDIRECTED
        >>> )
        >>> graph = Graph(
        >>>     "g1",
        >>>     data={"my": "graph", "data": "is", "very": "awesome"},
        >>>     nodes=set([n1, n2, n3, n4]),
        >>>     edges=set([e1, e2]),
        >>> )
        >>> edges = graph.outgoing_edges_of(n2)
        >>> edges == set([e2])
        >>> True

        \endcode
        """
        if not BaseGraphOps.is_in(g, n):
            raise ValueError("node not in Graph")

        eset = set()
        E = {e.id(): e for e in g.E}
        gdata = BaseGraphOps.to_edgelist(g)
        for eid in gdata[n.id()]:
            e = E[eid]
            if e.is_start(n):
                eset.add(e)
        return frozenset(eset)
예제 #6
0
    def incoming_edges_of(g: AbstractGraph,
                          n: AbstractNode) -> FrozenSet[AbstractEdge]:
        """!
        \brief obtain incoming edges of a given graph

        Incoming edges are defined as edges that end with the given node.
        We only check for the position and do not consider the type of the edge
        For its use case see \see outgoing_edges_of()
        \code{.py}

        >>> n1 = Node("n1", {})
        >>> n2 = Node("n2", {})
        >>> n3 = Node("n3", {})
        >>> n4 = Node("n4", {})
        >>> e1 = Edge(
        >>>     "e1", start_node=n1, end_node=n2, edge_type=EdgeType.UNDIRECTED
        >>> )
        >>> e2 = Edge(
        >>>     "e2", start_node=n2, end_node=n3, edge_type=EdgeType.UNDIRECTED
        >>> )
        >>> graph = Graph(
        >>>     "g1",
        >>>     data={"my": "graph", "data": "is", "very": "awesome"},
        >>>     nodes=set([n1, n2, n3, n4]),
        >>>     edges=set([e1, e2]),
        >>> )
        >>> edges = graph.incoming_edges_of(n2)
        >>> edges == set([e1])
        >>> True

        \endcode
        """
        if not BaseGraphOps.is_in(g, n):
            raise ValueError("node not in Graph")

        eset = set()
        gdata = BaseGraphOps.to_edgelist(g)
        E = {e.id(): e for e in g.E}
        for eid in gdata[n.id()]:
            e = E[eid]
            if e.is_end(n):
                eset.add(e)
        return frozenset(eset)
예제 #7
0
    def breadth_first_search(
        g: AbstractGraph,
        n1: AbstractNode,
        edge_generator: Callable[[AbstractNode], Set[AbstractEdge]],
    ) -> BaseGraphBFSResult:
        """!
        \brief find shortest path from given node to all other nodes

        Applies the Breadth first search algorithm from Even and Guy Even 2012, p. 12

        \throws ValueError if given node is not found in graph instance
        """
        if not BaseGraphOps.is_in(g, n1):
            raise ValueError("argument node is not in graph")
        nid = n1.id()
        Q = [nid]
        V: Dict[str, AbstractNode] = {v.id(): v for v in g.V}
        l_vs = {v: math.inf for v in V}
        l_vs[nid] = 0
        T = set([nid])
        P: Dict[str, Dict[str, str]] = {}
        P[nid] = {}
        while Q:
            u = Q.pop(0)
            unode = V[u]
            for edge in edge_generator(unode):
                vnode = edge.get_other(unode)
                vid = vnode.id()
                if vid not in T:
                    T.add(vid)
                    l_vs[vid] = int(l_vs[u] + 1)
                    P[nid][u] = vid
                    Q.append(vid)
        #
        T = set([V[t] for t in T])
        path_props = {"bfs-tree": P, "path-set": T, "top-sort": l_vs}
        return BaseGraphBFSResult(
            props=path_props,
            result_id="bfs-result-of-" + g.id(),
            search_name="breadth_first_search",
            data={},
        )
예제 #8
0
    def edges_of(g: AbstractGraph, n: AbstractNode) -> Set[AbstractEdge]:
        """!
        \brief obtain the edge set of a given node.

        \param n node whose adjacent edges we are interested in

        \return edge set of node.

        \throw ValueError if node is not in graph we raise a value error

        \code{.py}

        >>> n1 = Node("n1", {})
        >>> n2 = Node("n2", {})
        >>> n3 = Node("n3", {})
        >>> n4 = Node("n4", {})
        >>> e1 = Edge(
        >>>     "e1", start_node=n1, end_node=n2, edge_type=EdgeType.UNDIRECTED
        >>> )
        >>> e2 = Edge(
        >>>     "e2", start_node=n2, end_node=n3, edge_type=EdgeType.UNDIRECTED
        >>> )
        >>> graph = Graph(
        >>>     "g1",
        >>>     data={"my": "graph", "data": "is", "very": "awesome"},
        >>>     nodes=set([n1, n2, n3, n4]),
        >>>     edges=set([e1, e2]),
        >>> )
        >>> edges = graph.edges_of(n2)
        >>> edges == set([e1, e2])
        >>> True

        \endcode
        """

        if not BaseGraphOps.is_in(g, n):
            raise ValueError("node not in Graph")
        gdata = BaseGraphOps.to_edgelist(g)
        edge_ids = gdata[n.id()]
        E = {e.id(): e for e in g.E}
        return set([E[eid] for eid in edge_ids])