def test_get_components(self): """""" comps = self.ugraph4.get_components() cs = list(comps) cs0ns = BaseGraphOps.nodes(cs[0]) cs0es = BaseGraphOps.edges(cs[0]) # cs1ns = BaseGraphOps.nodes(cs[1]) cs1es = BaseGraphOps.edges(cs[1]) # # compare graphs # first component u2nodes = set([self.a, self.b, self.e, self.f]) u2edges = set([self.ab, self.af, self.ae, self.be, self.ef]) # second component g2node = set([self.n1, self.n2, self.n3, self.n4]) g2edge = set([self.e1, self.e2, self.e3, self.e4]) # cond1 = u2nodes == cs0ns or u2nodes == cs1ns # cond2 = g2node == cs0ns or g2node == cs1ns # cond3 = u2edges == cs0es or u2edges == cs1es cond4 = g2edge == cs0es or g2edge == cs1es self.assertTrue(cond1) self.assertTrue(cond2) self.assertTrue(cond3) self.assertTrue(cond4)
def is_proper_subgraph(g1: AbstractGraph, g2: AbstractGraph) -> bool: "check if g2 is subgraph of g1" ns = BaseGraphOps.nodes(g2) es = BaseGraphOps.edges(g2) contains_nodes = BaseGraphOps.contains(g1, ns) contains_edges = BaseGraphOps.contains(g1, es) return contains_edges and contains_nodes
def __init__( self, gid: str, nodes: Set[NumCatRVariable], edges: Set[Edge], factors: Optional[Set[Factor]] = None, data={}, ): """! \brief constructor for a generic Probabilistic Graphical Model The generic model that extends the #Graph definition by adding a new set, called set of factors. Most of the parameters are documented in #Graph. """ super().__init__(gid=gid, data=data, nodes=nodes, edges=edges) if factors is None: fs: Set[Factor] = set() for e in BaseGraphOps.edges(self): estart = e.start() eend = e.end() sdata = estart.data() edata = eend.data() evidences = set() if "evidence" in sdata: evidences.add((estart.id(), sdata["evidence"])) if "evidence" in edata: evidences.add((eend.id(), edata["evidence"])) f = Factor(gid=str(uuid4()), scope_vars=set([estart, eend])) if len(evidences) != 0: f = f.reduced_by_value(evidences) fs.add(f) self.Fs = fs else: self.Fs = factors
def is_induced_subgraph(g1: AbstractGraph, g2: AbstractGraph) -> bool: """ check if g2 is induced subgraph of g1 induced subgraph: g2 \sub g1 ^ xy \in Edge[g1] with x,y Vertex[g2] """ is_subgraph = BaseGraphAnalyzer.is_subgraph(g1, g2) if not is_subgraph: return False g2_vertices = BaseGraphOps.nodes(g2) g1_edges = BaseGraphOps.edges(g1) for g1_edge in g1_edges: has_node_id1 = False has_node_id2 = False edge_node_ids = g1_edge.node_ids() edge_node_id1 = edge_node_ids[0] edge_node_id2 = edge_node_ids[1] for g2_vertex in g2_vertices: vertex_id = g2_vertex.id() if vertex_id == edge_node_id1: has_node_id1 = True if vertex_id == edge_node_id2: has_node_id2 = True # if not has_node_id1 and not has_node_id2: return False return True
def from_digraph(cls, dig: DiGraph, fs: Optional[Set[Factor]]): """! \brief Construct a BayesianNetwork from a directed graph We assume that edges encode an independence structure of the system. Hence we deduce factors from them. If there is any evidence associated with a random variable, we apply them to reduce factors. \param dig A Directed Graph whose nodes are random variables \return BayesianNetwork \code{.py} >>> myDiGraph = DiGraph() \endcode """ if fs is None: fs = cls.deduce_factors_from_digraph(dig) # return BayesianNetwork( gid=str(uuid4()), nodes=BaseGraphOps.nodes(dig), edges=BaseGraphOps.edges(dig), factors=fs, )
def from_graph(cls, g: Graph): """! \brief Construct an undirected graph from given graph. \throws ValueError if the graph contains a directed edge. \param g source graph """ for e in BaseGraphOps.edges(g): if e.type() == EdgeType.DIRECTED: raise ValueError("Graph contains directed edges") return UndiGraph( gid=str(uuid4()), data=g.data(), nodes=BaseGraphOps.nodes(g), edges=BaseGraphOps.edges(g), )
def test__sub__e(self): """""" n = Node("n646", {}) e = Edge( "e8", start_node=self.n1, end_node=n, edge_type=EdgeType.UNDIRECTED ) g = self.graph - e self.assertEqual(BaseGraphOps.edges(g), set([self.e1, self.e2]))
def test_from_edgeset(self): """""" eset = set([self.e1, self.e2, self.e3, self.e4]) g = Graph.from_edgeset(eset) self.assertEqual( BaseGraphOps.nodes(g), set([self.n1, self.n2, self.n3, self.n4]) ) self.assertEqual(BaseGraphOps.edges(g), eset)
def test__add__g(self): """""" n = Node("n646", {}) n1 = Node("n647", {}) n2 = Node("n648", {}) e = Edge( "e8", start_node=self.n1, end_node=n, edge_type=EdgeType.UNDIRECTED ) gg = Graph(gid="temp", data={}, nodes=set([n, n1, n2]), edges=set([e])) g = self.graph + gg self.assertEqual( BaseGraphOps.nodes(g), set([self.n1, self.n2, self.n3, self.n4, n, n1, n2]), ) self.assertEqual(BaseGraphOps.edges(g), set([e, self.e1, self.e2]))
def test_subtract_g(self): n = Node("n646", {}) n1 = Node("n647", {}) n2 = Node("n648", {}) e = Edge( "e8", start_node=self.n1, end_node=n, edge_type=EdgeType.UNDIRECTED ) gg = BaseGraph( gid="temp", data={}, nodes=set([n, n1, n2]), edges=set([e, self.e1]), ) g = BaseGraphAlgOps.subtract(self.graph, gg) self.assertEqual(BaseGraphOps.edges(g), set([])) self.assertEqual(BaseGraphOps.nodes(g), set([self.n3, self.n4]))
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__)
def get_subgraph_by_vertices( self, vs: Set[Node], edge_policy: Callable[[Edge, Set[Node]], bool] = lambda x, ys: set( [x.start(), x.end()]).issubset(ys) is True, ) -> GraphObject: """! Get the subgraph using vertices. \param vs set of vertices for the subgraph \param edge_policy determines which edges should be conserved. By default we conserve edges whose incident nodes are a subset of vs """ es: Set[Edge] = set() for e in BaseGraphOps.edges(self): if edge_policy(e, vs) is True: es.add(e) return Graph.from_edge_node_set(edges=es, nodes=vs)
def set_op_node_edge( g: AbstractGraph, obj: Union[Set[AbstractNode], Set[AbstractEdge]], op: Callable[[Union[Set[AbstractNode], Set[AbstractEdge]]], Union[Set[AbstractNode], Set[AbstractEdge]], ], ): """!""" is_eset = all(isinstance(o, AbstractEdge) for o in obj) if is_eset: edges = BaseGraphOps.edges(g) return op(edges, obj) is_nset = all(isinstance(o, AbstractNode) for o in obj) if is_nset is False: raise TypeError("argument type is not supported: " + type(obj).__name__) # nodes = BaseGraphOps.nodes(g) return op(nodes, obj)
def has_self_loop(g: AbstractGraph) -> bool: """! \brief Check if graph has a self loop. We check whether the incident vertices of an edge is same. \code{.py} n1 = Node("n1", {}) n2 = Node("n2", {}) e1 = Edge( "e1", start_node=n1, end_node=n2, edge_type=EdgeType.UNDIRECTED ) e2 = Edge( "e1", start_node=n1, end_node=n1, edge_type=EdgeType.UNDIRECTED ) g = Graph("graph", nodes=set([n1, n2]), edges=set([e1, e2])) g.has_self_loop() # True \endcode """ for edge in BaseGraphOps.edges(g): if edge.start() == edge.end(): return True return False
def setUp(self): # self.n1 = Node("n1", {}) self.n2 = Node("n2", {}) self.n3 = Node("n3", {}) self.n4 = Node("n4", {}) self.n5 = Node("n5", {}) self.e1 = Edge( "e1", start_node=self.n1, end_node=self.n2, edge_type=EdgeType.UNDIRECTED, ) self.e2 = Edge( "e2", start_node=self.n2, end_node=self.n3, edge_type=EdgeType.UNDIRECTED, ) self.e3 = Edge( "e3", start_node=self.n3, end_node=self.n4, edge_type=EdgeType.UNDIRECTED, ) self.e4 = Edge( "e4", start_node=self.n1, end_node=self.n4, edge_type=EdgeType.UNDIRECTED, ) self.graph_2 = UndiGraph( "g2", data={ "my": "graph", "data": "is", "very": "awesome" }, nodes=set([self.n1, self.n2, self.n3, self.n4]), edges=set([self.e1, self.e2, self.e3]), ) self.a = Node("a", {}) # b self.b = Node("b", {}) # c self.c = Node("c", {}) self.d = Node("d", {}) self.f = Node("f", {}) # d self.e = Node("e", {}) # e self.g = Node("g", {}) self.h = Node("h", {}) self.ae = Edge( "ae", start_node=self.a, end_node=self.e, edge_type=EdgeType.UNDIRECTED, ) self.ab = Edge( "ab", start_node=self.a, end_node=self.b, data={"w": 1}, edge_type=EdgeType.UNDIRECTED, ) self.af = Edge( "af", start_node=self.a, end_node=self.f, edge_type=EdgeType.UNDIRECTED, ) self.ah = Edge( "ah", start_node=self.a, end_node=self.h, edge_type=EdgeType.UNDIRECTED, ) self.bh = Edge( "bh", start_node=self.b, end_node=self.h, edge_type=EdgeType.UNDIRECTED, ) self.be = Edge( "be", start_node=self.b, end_node=self.e, edge_type=EdgeType.UNDIRECTED, ) self.ef = Edge( "ef", data={"w": 5}, start_node=self.e, end_node=self.f, edge_type=EdgeType.UNDIRECTED, ) self.de = Edge( "de", data={"w": 4}, start_node=self.d, end_node=self.e, edge_type=EdgeType.UNDIRECTED, ) self.df = Edge( "df", data={"w": 8}, start_node=self.d, end_node=self.f, edge_type=EdgeType.UNDIRECTED, ) self.cd = Edge( "cd", data={"w": 3}, start_node=self.c, end_node=self.d, edge_type=EdgeType.UNDIRECTED, ) self.cg = Edge( "cg", start_node=self.c, end_node=self.g, edge_type=EdgeType.UNDIRECTED, ) self.gd = Edge( "gd", data={"w": 7}, start_node=self.g, end_node=self.d, edge_type=EdgeType.UNDIRECTED, ) self.bg = Edge( "bg", data={"w": 6}, start_node=self.b, end_node=self.g, edge_type=EdgeType.UNDIRECTED, ) self.fg = Edge( "fg", start_node=self.f, end_node=self.g, edge_type=EdgeType.UNDIRECTED, ) self.bc = Edge( "bc", start_node=self.b, end_node=self.c, data={"w": 2}, edge_type=EdgeType.UNDIRECTED, ) # undirected graph self.ugraph1 = UndiGraph( "ug1", data={ "my": "graph", "data": "is", "very": "awesome" }, nodes=set([self.a, self.b, self.e, self.f]), edges=set([ self.ae, # self.ab, self.af, # self.be, self.ef, ]), ) # ugraph1: # +-----+ # / \ # a b e # \ / # +-----f self.ugraph2 = UndiGraph( "ug2", data={ "my": "graph", "data": "is", "very": "awesome" }, nodes=set([self.a, self.b, self.e, self.f]), edges=set([ self.ae, self.ab, self.af, self.be, self.ef, ]), ) # ugraph2 : # +-----+ # / \ # a -- b -- e # \ / # +-----f self.ugraph3 = UndiGraph( "ug3", data={ "my": "graph", "data": "is", "very": "awesome" }, nodes=set([self.a, self.b, self.e, self.f]), edges=set([ self.ab, self.af, self.be, ]), ) # ugraph3 : # # # a -- b -- e # \ # +-----f self.ugraph4 = UndiGraph( "ug4", data={ "my": "graph", "data": "is", "very": "awesome" }, nodes=BaseGraphOps.nodes(self.ugraph2).union( BaseGraphOps.nodes(self.graph_2)), edges=BaseGraphOps.edges(self.ugraph2).union( BaseGraphOps.edges(self.graph_2)), ) # ugraph 4 # +-----+ n1 -- n2 -- n3 -- n4 # / \ \ / # a -- b -- e +--------------+ # \ / # +-----f self.ugraph5 = UndiGraph( "ug5", data={ "my": "graph", "data": "is", "very": "awesome" }, nodes=set([self.a, self.b, self.c, self.d, self.e, self.f, self.g]), edges=set([ self.ab, self.bc, self.bg, self.cd, self.gd, self.df, self.de, self.ef, ]), ) # ugraph 5 # +----c---+ +--e # / 2 3 \ / 4 | # a --- b d | 5 # 1 \ 6 7 / \ 8 | # +---g---+ +--f self.ugraph6 = UndiGraph( "ug6", data={ "my": "graph", "data": "is", "very": "awesome" }, nodes=set([ self.a, self.b, self.c, self.d, self.e, self.f, self.g, self.h, ]), edges=set([ self.ab, self.ah, self.bc, self.bh, self.cd, self.de, self.df, self.cg, self.fg, ]), ) # ugraph 6 # a--+ e----d # | \ / \ # | b----c f # | / \ / # h--+ g self.ad = Edge( "ad", start_node=self.a, end_node=self.d, edge_type=EdgeType.UNDIRECTED, ) # self.ugraph7 = UndiGraph( "ug7", nodes=set([self.a, self.b, self.c, self.d]), edges=set([self.ab, self.bc, self.cd, self.ad]), )
def to_adjmat(self, vtype=int) -> Dict[Tuple[str, str], int]: """! \brief Transform adjacency list to adjacency matrix representation \param vtype the cast type for the entry of adjacency matrix. \return adjacency matrix whose keys are identifiers of nodes and values are flags whether there is an edge between them. \code{.py} >>> a = Node("a", {}) # b >>> b = Node("b", {}) # c >>> f = Node("f", {}) # d >>> e = Node("e", {}) # e >>> ae = Edge( >>> "ae", start_node=a, end_node=e, edge_type=EdgeType.UNDIRECTED >>> ) >>> af = Edge( >>> "af", start_node=a, end_node=f, edge_type=EdgeType.UNDIRECTED >>> ) >>> ef = Edge( >>> "ef", start_node=e, end_node=f, edge_type=EdgeType.UNDIRECTED >>> ) >>> ugraph1 = Graph( >>> "graph", >>> data={"my": "graph", "data": "is", "very": "awesome"}, >>> nodes=set([a, b, e, f]), >>> edges=set([ae, af, ef]), >>> ) >>> mat = ugraph1.to_adjmat(vtype=bool) >>> mat == { >>> ("b", "b"): False, >>> ("b", "e"): False, >>> ("b", "f"): False, >>> ("b", "a"): False, >>> ("e", "b"): False, >>> ("e", "e"): False, >>> ("e", "f"): True, >>> ("e", "a"): True, >>> ("f", "b"): False, >>> ("f", "e"): True, >>> ("f", "f"): False, >>> ("f", "a"): True, >>> ("a", "b"): False, >>> ("a", "e"): True, >>> ("a", "f"): True, >>> ("a", "a"): False >>> } >>> True \endcode """ gmat = {} for v in self.V: for k in self.V: gmat[(v.id(), k.id())] = vtype(0) for edge in BaseGraphOps.edges(self): tpl1 = (edge.start().id(), edge.end().id()) tpl2 = (edge.end().id(), edge.start().id()) if tpl1 in gmat: gmat[tpl1] = vtype(1) if edge.type() == EdgeType.UNDIRECTED: if tpl2 in gmat: gmat[tpl2] = vtype(1) return gmat
def setUp(self): # self.n1 = Node("n1", {}) self.n2 = Node("n2", {}) self.n3 = Node("n3", {}) self.n4 = Node("n4", {}) self.n5 = Node("n5", {}) self.e1 = Edge.directed("e1", start_node=self.n1, end_node=self.n2) self.e2 = Edge.directed("e2", start_node=self.n2, end_node=self.n3) self.e3 = Edge.directed("e3", start_node=self.n3, end_node=self.n4) self.e4 = Edge.directed("e4", start_node=self.n1, end_node=self.n4) self.graph_2 = DiGraph( "g2", data={ "my": "graph", "data": "is", "very": "awesome" }, nodes=set([self.n1, self.n2, self.n3, self.n4]), edges=set([self.e1, self.e2, self.e3, self.e4]), ) # # n1 → n2 → n3 → n4 # | ↑ # +--------------+ self.a = Node("a", {}) # b self.b = Node("b", {}) # c self.c = Node("c", {}) self.d = Node("d", {}) self.f = Node("f", {}) # d self.e = Node("e", {}) # e self.g = Node("g", {}) self.h = Node("h", {}) self.ae = Edge.directed("ae", start_node=self.a, end_node=self.e) self.ab = Edge.directed("ab", start_node=self.a, end_node=self.b) self.af = Edge.directed("af", start_node=self.a, end_node=self.f) self.ah = Edge.directed("ah", start_node=self.a, end_node=self.h) self.bh = Edge.directed("bh", start_node=self.b, end_node=self.h) self.be = Edge.directed("be", start_node=self.b, end_node=self.e) self.ef = Edge.directed("ef", start_node=self.e, end_node=self.f) self.de = Edge.directed("de", start_node=self.d, end_node=self.e) self.df = Edge.directed("df", start_node=self.d, end_node=self.f) self.cd = Edge.directed("cd", start_node=self.c, end_node=self.d) self.cg = Edge.directed("cg", start_node=self.c, end_node=self.g) self.gd = Edge.directed("gd", start_node=self.g, end_node=self.d) self.bg = Edge.directed("bg", start_node=self.b, end_node=self.g) self.fg = Edge.directed("fg", start_node=self.f, end_node=self.g) self.bc = Edge.directed("bc", start_node=self.b, end_node=self.c) # directed graph self.dgraph1 = DiGraph( "dg1", data={ "my": "graph", "data": "is", "very": "awesome" }, nodes=set([self.a, self.b, self.e, self.f]), edges=set([ self.ae, # self.ab, self.af, # self.be, self.ef, ]), ) # dgraph1: # # # a --------> e b # | | # +---> f <---+ # self.dgraph2 = DiGraph( "dg2", data={ "my": "graph", "data": "is", "very": "awesome" }, nodes=set([self.a, self.b, self.e, self.f]), edges=set([ self.ae, self.ab, self.af, self.be, self.ef, ]), ) # dgraph2 : # # a -> b -> e -> f # | ↑ ↑ # +---------+----+ self.dgraph3 = DiGraph( "dg3", data={ "my": "graph", "data": "is", "very": "awesome" }, nodes=set([self.a, self.b, self.e, self.f]), edges=set([ self.ab, self.af, self.be, ]), ) # dgraph3 : # # a -> b -> e # \ # +---> f self.dgraph4 = DiGraph( "dg4", data={ "my": "graph", "data": "is", "very": "awesome" }, nodes=BaseGraphOps.nodes(self.dgraph2).union( BaseGraphOps.nodes(self.graph_2)), edges=BaseGraphOps.edges(self.dgraph2).union( BaseGraphOps.edges(self.graph_2)), ) # dgraph 4 # # a -> b -> e -> f n1 -> n2 -> n3 -> n4 # | ↑ ↑ | ↑ # +---------+----+ +------------------+ self.e_n = Edge.directed("en", start_node=self.e, end_node=self.n1) self.dgraph5 = DiGraph( "dg5", data={ "my": "graph", "data": "is", "very": "awesome" }, nodes=set([self.a, self.b, self.c, self.d, self.e, self.f, self.g]), edges=set([ self.ab, self.bc, self.bg, self.cd, self.gd, self.df, self.de, self.ef, ]), ) # dgraph 5 # +--> c +---> e # / \ / | # a -> b +--> d | # \ / \ v # +--> g +---> f self.dgraph6 = DiGraph( "dg6", data={ "my": "graph", "data": "is", "very": "awesome" }, nodes=set([ self.a, self.b, self.c, self.d, self.e, self.f, self.g, self.h, ]), edges=set([ self.ab, self.ah, self.bc, self.bh, self.cd, self.de, self.df, self.cg, self.fg, ]), )
def setUp(self): self.n1 = Node("n1", {}) self.n2 = Node("n2", {}) self.n3 = Node("n3", {}) self.n4 = Node("n4", {}) self.n5 = Node("n5", {}) self.e1 = Edge( "e1", start_node=self.n1, end_node=self.n2, edge_type=EdgeType.UNDIRECTED, ) self.e2 = Edge( "e2", start_node=self.n2, end_node=self.n3, edge_type=EdgeType.UNDIRECTED, ) self.e3 = Edge( "e3", start_node=self.n3, end_node=self.n4, edge_type=EdgeType.UNDIRECTED, ) self.e4 = Edge( "e4", start_node=self.n1, end_node=self.n4, edge_type=EdgeType.UNDIRECTED, ) self.graph = BaseGraph( "g1", data={"my": "graph", "data": "is", "very": "awesome"}, nodes=set([self.n1, self.n2, self.n3, self.n4]), edges=set([self.e1, self.e2]), ) self.graph_2 = BaseGraph( "g2", data={"my": "graph", "data": "is", "very": "awesome"}, nodes=set([self.n1, self.n2, self.n3, self.n4]), edges=set([self.e1, self.e2, self.e3]), ) # self.a = Node("a", {}) # b self.b = Node("b", {}) # c self.f = Node("f", {}) # d self.e = Node("e", {}) # e self.ae = Edge( "ae", start_node=self.a, end_node=self.e, edge_type=EdgeType.UNDIRECTED, ) self.ab = Edge( "ab", start_node=self.a, end_node=self.b, edge_type=EdgeType.UNDIRECTED, ) self.af = Edge( "af", start_node=self.a, end_node=self.f, edge_type=EdgeType.UNDIRECTED, ) self.be = Edge( "be", start_node=self.b, end_node=self.e, edge_type=EdgeType.UNDIRECTED, ) self.ef = Edge( "ef", start_node=self.e, end_node=self.f, edge_type=EdgeType.UNDIRECTED, ) # undirected graph self.ugraph2 = BaseGraph( "ug2", data={"my": "graph", "data": "is", "very": "awesome"}, nodes=set([self.a, self.b, self.e, self.f]), edges=set( [ self.ae, self.ab, self.af, self.be, self.ef, ] ), ) # ugraph2 : # +-----+ # / \ # a -- b -- e # \ / # +-----f self.ugraph3 = BaseGraph( "ug3", data={"my": "graph", "data": "is", "very": "awesome"}, nodes=set([self.a, self.b, self.e, self.f]), edges=set( [ self.ab, # self.af, self.be, ] ), ) # ugraph3 : # # # a -- b -- e # \ # +-----f self.ugraph4 = BaseGraph( "ug4", data={"my": "graph", "data": "is", "very": "awesome"}, nodes=set(BaseGraphOps.nodes(self.ugraph2)).union( BaseGraphOps.nodes(self.graph_2) ), edges=BaseGraphOps.edges(self.ugraph2).union( BaseGraphOps.edges(self.graph_2) ), ) # ugraph 4 # +-----+ n1 -- n2 -- n3 -- n4 # / \ \ / # a -- b -- e +--------------+ # \ / # +-----f # make some directed edges self.bb = Node("bb", {}) self.cc = Node("cc", {}) self.dd = Node("dd", {}) self.ee = Node("ee", {}) self.bb_cc = Edge( "bb_cc", start_node=self.bb, end_node=self.cc, edge_type=EdgeType.DIRECTED, ) self.cc_dd = Edge( "cc_dd", start_node=self.cc, end_node=self.dd, edge_type=EdgeType.DIRECTED, ) self.dd_ee = Edge( "dd_ee", start_node=self.dd, end_node=self.ee, edge_type=EdgeType.DIRECTED, ) self.ee_bb = Edge( "ee_bb", start_node=self.ee, end_node=self.bb, edge_type=EdgeType.DIRECTED, ) self.bb_dd = Edge( "bb_dd", start_node=self.bb, end_node=self.dd, edge_type=EdgeType.DIRECTED, )