Esempio n. 1
0
 def from_abstract_graph(cls, g_: AbstractGraph):
     "Obtain base graph from AbstractGraph implementing object"
     if issubclass(g_, AbstractGraph):
         raise TypeError("Argument must implement AbstractGraph interface")
     nodes = set(g_.V)
     edges = set(g_.E)
     data = g_.data()
     gid = g_.id()
     return BaseGraph(gid=gid, data=data, nodes=nodes, edges=edges)
Esempio n. 2
0
    def is_stable(g: AbstractGraph, ns: FrozenSet[AbstractNode]) -> bool:
        """!
        \brief check if given node set is stable

        We ensure that no nodes in the given node set is a neighbour of one
        another as per the definition of Diestel 2017, p. 3.

        \throws ValueError if argument node set is not a subset of vertices of
        the graph
        \code{.py}

        >>> n1 = Node("n1", {})
        >>> n2 = Node("n2", {})
        >>> n3 = Node("n3", {})
        >>> n4 = Node("n4", {})
        >>> n5 = Node("n5", {})
        >>> 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, n5]),
        >>>   edges=set([e1, e2, e3]),
        >>> )
        >>> graph_2.is_stable(set([n1, n3, n5]))
        >>> True

        \endcode
        """
        if ns.issubset(BaseGraphOps.nodes(g)) is False:
            raise ValueError("node set is not contained in graph")
        node_list = list(ns)
        while node_list:
            n1 = node_list.pop()
            for n2 in node_list:
                if g.is_neighbour_of(n1=n1, n2=n2):
                    return False
        return True
Esempio n. 3
0
    def set_op(
        g: AbstractGraph,
        obj: Union[Set[AbstractNode], Set[AbstractEdge], AbstractGraph,
                   AbstractNode, AbstractEdge, ],
        op: Callable[[Union[Set[AbstractNode], Set[AbstractEdge]]],
                     Union[Set[AbstractNode], Set[AbstractEdge],
                           AbstractGraph], ],
    ) -> Optional[Union[Set[AbstractNode], Set[AbstractEdge], bool]]:
        """!
        \brief generic set operation for graph

        \param obj the hooked object to operation. We deduce its corresponding
        argument from its type.
        \param op operation that is going to be applied to obj and its
        corresponding object.

        The idea is to give a single interface for generic set operation
        functions. For example if object is a set of nodes we provide
        the target for the operation as the nodes of this graph, if it is an
        edge we provide a set of edges of this graph
        """
        is_node = isinstance(obj, AbstractNode)
        if is_node:
            return BaseGraphSetOps.set_op_node_edge(g=g, obj=set([obj]), op=op)
        is_edge = isinstance(obj, AbstractEdge)
        if is_edge:
            return BaseGraphSetOps.set_op_node_edge(g=g, obj=set([obj]), op=op)
        is_set = isinstance(obj, (set, frozenset))
        if is_set:
            return BaseGraphSetOps.set_op_node_edge(g=g, obj=obj, op=op)
        is_graph = isinstance(obj, AbstractGraph)
        if is_graph:
            oeset = BaseGraphOps.edges(obj)
            onset = BaseGraphOps.nodes(obj)
            oedge_set = BaseGraphSetOps.set_op(g, obj=oeset, op=op)
            onode_set = BaseGraphSetOps.set_op(g, obj=onset, op=op)
            gdata = g.data()
            gdata.update(obj.data())
            return BaseGraph(gid=str(uuid4()),
                             nodes=onode_set,
                             edges=oedge_set,
                             data=gdata)
        else:
            raise TypeError("argument type is not supported: " +
                            type(obj).__name__)
Esempio n. 4
0
    def neighbours_of(g: AbstractGraph, n1: AbstractEdge) -> Set[AbstractNode]:
        """!
        \brief obtain neighbour set of a given node.

        \param n1 the node whose neighbour set we are searching for

        \throws ValueError if node is not inside the graph

        \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]),
        >>> )
        >>> neighbours = graph_2.neighbours_of(n2)
        >>> [n.id() for n in neighbours]
        >>> ["n1", "n3"]

        \endcode
        """
        if not BaseGraphOps.is_in(g, n1):
            raise ValueError("node is not in graph")
        neighbours = set()
        for n2 in BaseGraphOps.nodes(g):
            if g.is_neighbour_of(n1=n1, n2=n2) is True:
                neighbours.add(n2)
        return neighbours
Esempio n. 5
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={},
        )
Esempio n. 6
0
    def plus_minus_node_edge(
        g: AbstractGraph,
        el: Union[Set[AbstractNode], Set[AbstractEdge], AbstractGraph],
        is_plus=False,
    ) -> BaseGraph:
        """!
        \brief subtraction of elements for G - v cases, see Diestel p. 4
        """
        if isinstance(el, AbstractGraph):
            if is_plus is False:
                elnodes = set(el.V)
                nodes = {n for n in g.V if n not in elnodes}
                bg = BaseGraph.based_on_node_set(edges=set(g.E), nodes=nodes)
                bg.update_data(g.data())
                return bg
            else:
                nodes = set(g.V).union(el.V)
                edges = set(g.E).union(el.E)
                bg = BaseGraph.from_edge_node_set(edges=edges, nodes=nodes)
                bg.update_data(g.data())
                return bg

        nset = all(isinstance(e, AbstractNode) for e in el)
        if nset:
            if is_plus is False:
                nodes = {n for n in g.V if n not in el}
                edges = set(g.E)
                bg = BaseGraph.based_on_node_set(edges=edges, nodes=nodes)
                bg.update_data(g.data())
                return bg
            else:
                nodes = set(g.V).union(el)
                edges = set(g.E)
                bg = BaseGraph.based_on_node_set(edges=edges, nodes=nodes)
                bg.update_data(g.data())
                return bg
        eset = all(isinstance(e, AbstractEdge) for e in el)
        if eset:
            if is_plus is False:
                edges = {e for e in g.E if e not in el}
                bg = BaseGraph.from_edge_node_set(edges=edges, nodes=set(g.V))
                bg.update_data(g.data())
                return bg
            else:
                edges = set(g.E).union(el)
                bg = BaseGraph.from_edge_node_set(edges=edges, nodes=set(g.V))
                bg.update_data(g.data())
                return bg
Esempio n. 7
0
    def is_node_independent_of(g: AbstractGraph, n1: AbstractNode,
                               n2: AbstractNode) -> bool:
        """!
        \brief check if two nodes are independent
        We consider two nodes independent if they are not the same, and they
        are not neighbours.

        \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_node_independent_of(n1, n3)
        >>> True

        \endcode
        """
        if n1 == n2:
            return False
        return True if g.is_neighbour_of(n1, n2) is False else False
Esempio n. 8
0
 def is_disjoint(g1: AbstractGraph, g2: AbstractGraph) -> bool:
     "check if g2 is disjoint to g1"
     ns = BaseGraphOps.nodes(g1)
     ns_ = g2.vertex_intersection(ns)
     return len(ns_) == 0
Esempio n. 9
0
    def depth_first_search(
        g: AbstractGraph,
        edge_generator: Callable[[AbstractNode], Set[AbstractNode]],
        check_cycle: bool = False,
        start_node: Optional[AbstractNode] = None,
    ) -> BaseGraphDFSResult:
        """!
        \brief interior visit function for depth first enumeration of graph
        instance.

        \see dfs_forest() method for more information on parameters.
        """
        V: Dict[str, AbstractNode] = {n.id(): n for n in g.V}
        if start_node is not None:
            if not BaseGraphOps.is_in(g, start_node):
                raise ValueError("Specified start node not in graph")
            #
            Vlst: List[str] = list(v for v in V.keys() if v != start_node.id())
            Vlst.insert(0, start_node.id())
            Vlst.sort()
        else:
            Vlst = list(v for v in V.keys())
            Vlst.sort()
        time = 0
        marked: Dict[str, bool] = {n: False for n in V}
        preds: Dict[str, Dict[str, str]] = {}
        Ts: Dict[str, Set[str]] = {}
        d: Dict[str, int] = {n: math.inf for n in V}
        f: Dict[str, int] = {n: math.inf for n in V}
        cycles: Dict[str, List[Dict[str, Union[str, int]]]] = {
            n: [] for n in V
        }
        component_counter = 0
        #
        for u in Vlst:
            if marked[u] is False:
                pred: Dict[str, Optional[str]] = {n: None for n in V}
                T: Set[str] = set()
                BaseGraphSearcher.dfs_forest(
                    g=g,
                    V=V,
                    u=u,
                    pred=pred,
                    cycles=cycles,
                    marked=marked,
                    d=d,
                    T=T,
                    f=f,
                    time=time,
                    check_cycle=check_cycle,
                    edge_generator=edge_generator,
                )
                component_counter += 1
                for child, parent in pred.copy().items():
                    if child != u and child is None:
                        pred.pop(child)
                Ts[u] = T
                preds[u] = pred
        #
        res = {
            "dfs-forest": BaseGraphSearcher.from_preds_to_edgeset(g, preds),
            "dfs-trees": preds,
            "first-visit-times": d,
            "last-visit-times": f,
            "components": Ts,
            "cycle-info": cycles,
            "nb-component": component_counter,
        }
        return BaseGraphDFSResult(
            props=res,
            result_id="dfs-result-of-" + g.id(),
            search_name="depth_first_search",
            data={},
        )