def __init__( self, gid: str, nodes: Union[Set[AbstractNode], FrozenSet[AbstractNode]], edges: Union[Set[AbstractEdge], FrozenSet[AbstractEdge]], data={}, ): super().__init__(oid=gid, odata=data) if not isinstance(nodes, (frozenset, set)): raise TypeError("Nodes must be a set or a frozenset") if not isinstance(edges, (frozenset, set)): raise TypeError("Edges must be a set or a frozenset") self._nodes: FrozenSet[AbstractNode] = BaseGraphOps.get_nodes( ns=nodes, es=edges ) self._edges: FrozenSet[AbstractEdge] = frozenset(edges) # self.gdata: Dict[str, List[str]] = BaseGraphOps.to_edgelist(self) if self._nodes is not None: self.is_empty = len(self._nodes) == 0 else: self.is_empty = True is_trivial = BaseGraphAnalyzer.is_trivial(self) if is_trivial: msg = "This library is not compatible with computations with trivial graph" msg += "\nNodes: " msg += str([n.id() for n in self.V]) msg += "\nEdges: " + str([e.id() for e in self.E]) raise ValueError(msg)
def test_to_edgelist(self): gdata = BaseGraphOps.to_edgelist(self.graph) mdata = gdata gdata = {"n4": [], "n1": ["e1"], "n3": ["e2"], "n2": ["e1", "e2"]} for k, vs in mdata.copy().items(): self.assertEqual(k in mdata, k in gdata) for v in vs: self.assertEqual(v in mdata[k], v in gdata[k])
def max_degree_vs(g: AbstractGraph) -> Set[AbstractNode]: """! \brief obtain vertex set of whose degrees are equal to maximum degree. """ md = BaseGraphAnalyzer.max_degree(g) gdata = BaseGraphOps.to_edgelist(g) nodes = set([v for v in g.V if len(gdata[v.id()]) == md]) return nodes
def min_degree_vs(g: AbstractGraph) -> Set[AbstractNode]: """! \brief obtain set of vertices whose degree equal to minimum degree of graph instance """ md = BaseGraphAnalyzer.min_degree(g) gdata = BaseGraphOps.to_edgelist(g) nodes = set([v for v in g.V if len(gdata[v.id()]) == md]) return nodes
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)
def average_degree(g: AbstractGraph) -> float: """! \brief obtain the average degree of graph instance The average degree is calculated using the formula: \f[ d(G) = \frac{1}{V[G]} \sum_{v \in V[G]} d(v) \f] It can be found in Diestel 2017, p. 5 """ gdata = BaseGraphOps.to_edgelist(g) return sum([len(gdata[v.id()]) for v in g.V]) / len(g.V)
def comp_degree(g: AbstractGraph, fn: Callable[[int, int], bool], comp_val: int) -> int: """! \brief generic comparison function for degree related operations It is used in the context of finding maximum or minimum degree of the graph instance. """ compare_v = comp_val gdata = BaseGraphOps.to_edgelist(g) for nid in g.V: nb_edges = len(gdata[nid.id()]) if fn(nb_edges, compare_v): compare_v = nb_edges return compare_v
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