Source code for pgmpy.base.UndirectedGraph

#!/usr/bin/env python3
"""Base class for undirected graphical models in pgmpy."""

import itertools

import networkx as nx


[docs] class UndirectedGraph(nx.Graph): """Base class for all the Undirected Graphical models. Each node in the graph can represent either a random variable, `Factor`, or a cluster of random variables. Edges in the graph are interactions between the nodes. Parameters ---------- data: input graph Data to initialize graph. If data=None (default) an empty graph is created. The data can be an edge list or any Networkx graph object. Examples -------- Create an empty UndirectedGraph with no nodes and no edges >>> from pgmpy.base import UndirectedGraph >>> G = UndirectedGraph() G can be grown in several ways **Nodes:** Add one node at a time: >>> G.add_node("a") Add the nodes from any container (a list, set or tuple or the nodes from another graph). >>> G.add_nodes_from(["a", "b"]) **Edges:** G can also be grown by adding edges. Add one edge, >>> G.add_edge("a", "b") a list of edges, >>> G.add_edges_from([("a", "b"), ("b", "c")]) If some edges connect nodes not yet in the model, the nodes are added automatically. There are no errors when adding nodes or edges that already exist. **Shortcuts:** Many common graph features allow python syntax for speed reporting. >>> "a" in G # check if node in graph True >>> len(G) # number of nodes in graph 3 """ def __init__(self, ebunch=None): """Initialize UndirectedGraph with optional edges.""" super().__init__(ebunch)
[docs] def add_edge(self, u, v, weight=None): """Add an edge between u and v. The nodes u and v will be automatically added if they are not already in the graph. Parameters ---------- u, v : nodes Nodes can be any hashable Python object. weight: int, float (default=None) The weight of the edge. Examples -------- >>> from pgmpy.base import UndirectedGraph >>> G = UndirectedGraph() >>> G.add_nodes_from(["Alice", "Bob", "Charles"]) >>> G.add_edge(u="Alice", v="Bob") >>> sorted(G.nodes()) ['Alice', 'Bob', 'Charles'] >>> sorted(G.edges()) [('Alice', 'Bob')] When the node is not already present in the graph: >>> G.add_edge(u="Alice", v="Ankur") >>> sorted(G.nodes()) ['Alice', 'Ankur', 'Bob', 'Charles'] >>> sorted(G.edges()) [('Alice', 'Ankur'), ('Alice', 'Bob')] Adding edges with weight: >>> G.add_edge("Ankur", "Maria", weight=0.1) >>> G.edges["Ankur", "Maria"] {'weight': 0.1} """ super().add_edge(u, v, weight=weight)
[docs] def add_edges_from(self, ebunch, weights=None): """Add all the edges in ebunch. If nodes referred in the ebunch are not already present, they will be automatically added. Node names can be any hashable python object. **The behavior of adding weights is different than networkx. Parameters ---------- ebunch : container of edges Each edge given in the container will be added to the graph. The edges must be given as 2-tuples (u, v). weights: list, tuple (default=None) A container of weights (int, float). The weight value at index i is associated with the edge at index i. Examples -------- >>> from pgmpy.base import UndirectedGraph >>> G = UndirectedGraph() >>> G.add_nodes_from(["Alice", "Bob", "Charles"]) >>> G.add_edges_from(ebunch=[("Alice", "Bob"), ("Bob", "Charles")]) >>> sorted(G.nodes()) ['Alice', 'Bob', 'Charles'] >>> sorted(G.edges()) [('Alice', 'Bob'), ('Bob', 'Charles')] When the node is not already in the model: >>> G.add_edges_from(ebunch=[("Alice", "Ankur")]) >>> sorted(G.nodes()) ['Alice', 'Ankur', 'Bob', 'Charles'] >>> sorted(G.edges()) [('Alice', 'Ankur'), ('Alice', 'Bob'), ('Bob', 'Charles')] Adding edges with weights: >>> G.add_edges_from( ... [("Ankur", "Maria"), ("Maria", "Mason")], weights=[0.3, 0.5] ... ) >>> G.edges["Ankur", "Maria"] {'weight': 0.3} >>> G.edges["Maria", "Mason"] {'weight': 0.5} """ ebunch = list(ebunch) if weights: if len(ebunch) != len(weights): raise ValueError("The number of elements in ebunch and weightsshould be equal") for index in range(len(ebunch)): self.add_edge(ebunch[index][0], ebunch[index][1], weight=weights[index]) else: for edge in ebunch: self.add_edge(edge[0], edge[1])
[docs] def is_clique(self, nodes): """Check if the given nodes form a clique. Parameters ---------- nodes: list, array-like List of nodes to check if they are a part of any clique. Examples -------- >>> from pgmpy.base import UndirectedGraph >>> G = UndirectedGraph( ... ebunch=[ ... ("A", "B"), ... ("C", "B"), ... ("B", "D"), ... ("B", "E"), ... ("D", "E"), ... ("E", "F"), ... ("D", "F"), ... ("B", "F"), ... ] ... ) >>> G.is_clique(nodes=["A", "B", "C", "D"]) False >>> G.is_clique(nodes=["B", "D", "E", "F"]) True Since B, D, E and F are clique, any subset of these should also be clique. >>> G.is_clique(nodes=["D", "E", "B"]) True """ for node1, node2 in itertools.combinations(nodes, 2): if not self.has_edge(node1, node2): return False return True
[docs] def is_triangulated(self): """Check whether the undirected graph is triangulated (chordal) or not. Chordal Graph: A chordal graph is one in which all cycles of four or more vertices have a chord. Examples -------- >>> from pgmpy.base import UndirectedGraph >>> G = UndirectedGraph() >>> G.add_edges_from( ... ebunch=[("x1", "x2"), ("x1", "x3"), ("x2", "x4"), ("x3", "x4")] ... ) >>> G.is_triangulated() False >>> G.add_edge(u="x1", v="x4") >>> G.is_triangulated() True """ return nx.is_chordal(self)