123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- """
- ==========================
- Bipartite Graph Algorithms
- ==========================
- """
- import networkx as nx
- from networkx.algorithms.components import connected_components
- from networkx.exception import AmbiguousSolution
- __all__ = [
- "is_bipartite",
- "is_bipartite_node_set",
- "color",
- "sets",
- "density",
- "degrees",
- ]
- def color(G):
- """Returns a two-coloring of the graph.
- Raises an exception if the graph is not bipartite.
- Parameters
- ----------
- G : NetworkX graph
- Returns
- -------
- color : dictionary
- A dictionary keyed by node with a 1 or 0 as data for each node color.
- Raises
- ------
- NetworkXError
- If the graph is not two-colorable.
- Examples
- --------
- >>> from networkx.algorithms import bipartite
- >>> G = nx.path_graph(4)
- >>> c = bipartite.color(G)
- >>> print(c)
- {0: 1, 1: 0, 2: 1, 3: 0}
- You can use this to set a node attribute indicating the biparite set:
- >>> nx.set_node_attributes(G, c, "bipartite")
- >>> print(G.nodes[0]["bipartite"])
- 1
- >>> print(G.nodes[1]["bipartite"])
- 0
- """
- if G.is_directed():
- import itertools
- def neighbors(v):
- return itertools.chain.from_iterable([G.predecessors(v), G.successors(v)])
- else:
- neighbors = G.neighbors
- color = {}
- for n in G: # handle disconnected graphs
- if n in color or len(G[n]) == 0: # skip isolates
- continue
- queue = [n]
- color[n] = 1 # nodes seen with color (1 or 0)
- while queue:
- v = queue.pop()
- c = 1 - color[v] # opposite color of node v
- for w in neighbors(v):
- if w in color:
- if color[w] == color[v]:
- raise nx.NetworkXError("Graph is not bipartite.")
- else:
- color[w] = c
- queue.append(w)
- # color isolates with 0
- color.update(dict.fromkeys(nx.isolates(G), 0))
- return color
- @nx._dispatch
- def is_bipartite(G):
- """Returns True if graph G is bipartite, False if not.
- Parameters
- ----------
- G : NetworkX graph
- Examples
- --------
- >>> from networkx.algorithms import bipartite
- >>> G = nx.path_graph(4)
- >>> print(bipartite.is_bipartite(G))
- True
- See Also
- --------
- color, is_bipartite_node_set
- """
- try:
- color(G)
- return True
- except nx.NetworkXError:
- return False
- def is_bipartite_node_set(G, nodes):
- """Returns True if nodes and G/nodes are a bipartition of G.
- Parameters
- ----------
- G : NetworkX graph
- nodes: list or container
- Check if nodes are a one of a bipartite set.
- Examples
- --------
- >>> from networkx.algorithms import bipartite
- >>> G = nx.path_graph(4)
- >>> X = set([1, 3])
- >>> bipartite.is_bipartite_node_set(G, X)
- True
- Notes
- -----
- An exception is raised if the input nodes are not distinct, because in this
- case some bipartite algorithms will yield incorrect results.
- For connected graphs the bipartite sets are unique. This function handles
- disconnected graphs.
- """
- S = set(nodes)
- if len(S) < len(nodes):
- # this should maybe just return False?
- raise AmbiguousSolution(
- "The input node set contains duplicates.\n"
- "This may lead to incorrect results when using it in bipartite algorithms.\n"
- "Consider using set(nodes) as the input"
- )
- for CC in (G.subgraph(c).copy() for c in connected_components(G)):
- X, Y = sets(CC)
- if not (
- (X.issubset(S) and Y.isdisjoint(S)) or (Y.issubset(S) and X.isdisjoint(S))
- ):
- return False
- return True
- def sets(G, top_nodes=None):
- """Returns bipartite node sets of graph G.
- Raises an exception if the graph is not bipartite or if the input
- graph is disconnected and thus more than one valid solution exists.
- See :mod:`bipartite documentation <networkx.algorithms.bipartite>`
- for further details on how bipartite graphs are handled in NetworkX.
- Parameters
- ----------
- G : NetworkX graph
- top_nodes : container, optional
- Container with all nodes in one bipartite node set. If not supplied
- it will be computed. But if more than one solution exists an exception
- will be raised.
- Returns
- -------
- X : set
- Nodes from one side of the bipartite graph.
- Y : set
- Nodes from the other side.
- Raises
- ------
- AmbiguousSolution
- Raised if the input bipartite graph is disconnected and no container
- with all nodes in one bipartite set is provided. When determining
- the nodes in each bipartite set more than one valid solution is
- possible if the input graph is disconnected.
- NetworkXError
- Raised if the input graph is not bipartite.
- Examples
- --------
- >>> from networkx.algorithms import bipartite
- >>> G = nx.path_graph(4)
- >>> X, Y = bipartite.sets(G)
- >>> list(X)
- [0, 2]
- >>> list(Y)
- [1, 3]
- See Also
- --------
- color
- """
- if G.is_directed():
- is_connected = nx.is_weakly_connected
- else:
- is_connected = nx.is_connected
- if top_nodes is not None:
- X = set(top_nodes)
- Y = set(G) - X
- else:
- if not is_connected(G):
- msg = "Disconnected graph: Ambiguous solution for bipartite sets."
- raise nx.AmbiguousSolution(msg)
- c = color(G)
- X = {n for n, is_top in c.items() if is_top}
- Y = {n for n, is_top in c.items() if not is_top}
- return (X, Y)
- def density(B, nodes):
- """Returns density of bipartite graph B.
- Parameters
- ----------
- B : NetworkX graph
- nodes: list or container
- Nodes in one node set of the bipartite graph.
- Returns
- -------
- d : float
- The bipartite density
- Examples
- --------
- >>> from networkx.algorithms import bipartite
- >>> G = nx.complete_bipartite_graph(3, 2)
- >>> X = set([0, 1, 2])
- >>> bipartite.density(G, X)
- 1.0
- >>> Y = set([3, 4])
- >>> bipartite.density(G, Y)
- 1.0
- Notes
- -----
- The container of nodes passed as argument must contain all nodes
- in one of the two bipartite node sets to avoid ambiguity in the
- case of disconnected graphs.
- See :mod:`bipartite documentation <networkx.algorithms.bipartite>`
- for further details on how bipartite graphs are handled in NetworkX.
- See Also
- --------
- color
- """
- n = len(B)
- m = nx.number_of_edges(B)
- nb = len(nodes)
- nt = n - nb
- if m == 0: # includes cases n==0 and n==1
- d = 0.0
- else:
- if B.is_directed():
- d = m / (2 * nb * nt)
- else:
- d = m / (nb * nt)
- return d
- def degrees(B, nodes, weight=None):
- """Returns the degrees of the two node sets in the bipartite graph B.
- Parameters
- ----------
- B : NetworkX graph
- nodes: list or container
- Nodes in one node set of the bipartite graph.
- weight : string or None, optional (default=None)
- The edge attribute that holds the numerical value used as a weight.
- If None, then each edge has weight 1.
- The degree is the sum of the edge weights adjacent to the node.
- Returns
- -------
- (degX,degY) : tuple of dictionaries
- The degrees of the two bipartite sets as dictionaries keyed by node.
- Examples
- --------
- >>> from networkx.algorithms import bipartite
- >>> G = nx.complete_bipartite_graph(3, 2)
- >>> Y = set([3, 4])
- >>> degX, degY = bipartite.degrees(G, Y)
- >>> dict(degX)
- {0: 2, 1: 2, 2: 2}
- Notes
- -----
- The container of nodes passed as argument must contain all nodes
- in one of the two bipartite node sets to avoid ambiguity in the
- case of disconnected graphs.
- See :mod:`bipartite documentation <networkx.algorithms.bipartite>`
- for further details on how bipartite graphs are handled in NetworkX.
- See Also
- --------
- color, density
- """
- bottom = set(nodes)
- top = set(B) - bottom
- return (B.degree(top, weight), B.degree(bottom, weight))
|