aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/networkx/algorithms/clique.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/networkx/algorithms/clique.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/networkx/algorithms/clique.py')
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/clique.py755
1 files changed, 755 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/clique.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/clique.py
new file mode 100644
index 00000000..57b588ae
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/clique.py
@@ -0,0 +1,755 @@
+"""Functions for finding and manipulating cliques.
+
+Finding the largest clique in a graph is NP-complete problem, so most of
+these algorithms have an exponential running time; for more information,
+see the Wikipedia article on the clique problem [1]_.
+
+.. [1] clique problem:: https://en.wikipedia.org/wiki/Clique_problem
+
+"""
+
+from collections import defaultdict, deque
+from itertools import chain, combinations, islice
+
+import networkx as nx
+from networkx.utils import not_implemented_for
+
+__all__ = [
+ "find_cliques",
+ "find_cliques_recursive",
+ "make_max_clique_graph",
+ "make_clique_bipartite",
+ "node_clique_number",
+ "number_of_cliques",
+ "enumerate_all_cliques",
+ "max_weight_clique",
+]
+
+
+@not_implemented_for("directed")
+@nx._dispatchable
+def enumerate_all_cliques(G):
+ """Returns all cliques in an undirected graph.
+
+ This function returns an iterator over cliques, each of which is a
+ list of nodes. The iteration is ordered by cardinality of the
+ cliques: first all cliques of size one, then all cliques of size
+ two, etc.
+
+ Parameters
+ ----------
+ G : NetworkX graph
+ An undirected graph.
+
+ Returns
+ -------
+ iterator
+ An iterator over cliques, each of which is a list of nodes in
+ `G`. The cliques are ordered according to size.
+
+ Notes
+ -----
+ To obtain a list of all cliques, use
+ `list(enumerate_all_cliques(G))`. However, be aware that in the
+ worst-case, the length of this list can be exponential in the number
+ of nodes in the graph (for example, when the graph is the complete
+ graph). This function avoids storing all cliques in memory by only
+ keeping current candidate node lists in memory during its search.
+
+ The implementation is adapted from the algorithm by Zhang, et
+ al. (2005) [1]_ to output all cliques discovered.
+
+ This algorithm ignores self-loops and parallel edges, since cliques
+ are not conventionally defined with such edges.
+
+ References
+ ----------
+ .. [1] Yun Zhang, Abu-Khzam, F.N., Baldwin, N.E., Chesler, E.J.,
+ Langston, M.A., Samatova, N.F.,
+ "Genome-Scale Computational Approaches to Memory-Intensive
+ Applications in Systems Biology".
+ *Supercomputing*, 2005. Proceedings of the ACM/IEEE SC 2005
+ Conference, pp. 12, 12--18 Nov. 2005.
+ <https://doi.org/10.1109/SC.2005.29>.
+
+ """
+ index = {}
+ nbrs = {}
+ for u in G:
+ index[u] = len(index)
+ # Neighbors of u that appear after u in the iteration order of G.
+ nbrs[u] = {v for v in G[u] if v not in index}
+
+ queue = deque(([u], sorted(nbrs[u], key=index.__getitem__)) for u in G)
+ # Loop invariants:
+ # 1. len(base) is nondecreasing.
+ # 2. (base + cnbrs) is sorted with respect to the iteration order of G.
+ # 3. cnbrs is a set of common neighbors of nodes in base.
+ while queue:
+ base, cnbrs = map(list, queue.popleft())
+ yield base
+ for i, u in enumerate(cnbrs):
+ # Use generators to reduce memory consumption.
+ queue.append(
+ (
+ chain(base, [u]),
+ filter(nbrs[u].__contains__, islice(cnbrs, i + 1, None)),
+ )
+ )
+
+
+@not_implemented_for("directed")
+@nx._dispatchable
+def find_cliques(G, nodes=None):
+ """Returns all maximal cliques in an undirected graph.
+
+ For each node *n*, a *maximal clique for n* is a largest complete
+ subgraph containing *n*. The largest maximal clique is sometimes
+ called the *maximum clique*.
+
+ This function returns an iterator over cliques, each of which is a
+ list of nodes. It is an iterative implementation, so should not
+ suffer from recursion depth issues.
+
+ This function accepts a list of `nodes` and only the maximal cliques
+ containing all of these `nodes` are returned. It can considerably speed up
+ the running time if some specific cliques are desired.
+
+ Parameters
+ ----------
+ G : NetworkX graph
+ An undirected graph.
+
+ nodes : list, optional (default=None)
+ If provided, only yield *maximal cliques* containing all nodes in `nodes`.
+ If `nodes` isn't a clique itself, a ValueError is raised.
+
+ Returns
+ -------
+ iterator
+ An iterator over maximal cliques, each of which is a list of
+ nodes in `G`. If `nodes` is provided, only the maximal cliques
+ containing all the nodes in `nodes` are returned. The order of
+ cliques is arbitrary.
+
+ Raises
+ ------
+ ValueError
+ If `nodes` is not a clique.
+
+ Examples
+ --------
+ >>> from pprint import pprint # For nice dict formatting
+ >>> G = nx.karate_club_graph()
+ >>> sum(1 for c in nx.find_cliques(G)) # The number of maximal cliques in G
+ 36
+ >>> max(nx.find_cliques(G), key=len) # The largest maximal clique in G
+ [0, 1, 2, 3, 13]
+
+ The size of the largest maximal clique is known as the *clique number* of
+ the graph, which can be found directly with:
+
+ >>> max(len(c) for c in nx.find_cliques(G))
+ 5
+
+ One can also compute the number of maximal cliques in `G` that contain a given
+ node. The following produces a dictionary keyed by node whose
+ values are the number of maximal cliques in `G` that contain the node:
+
+ >>> pprint({n: sum(1 for c in nx.find_cliques(G) if n in c) for n in G})
+ {0: 13,
+ 1: 6,
+ 2: 7,
+ 3: 3,
+ 4: 2,
+ 5: 3,
+ 6: 3,
+ 7: 1,
+ 8: 3,
+ 9: 2,
+ 10: 2,
+ 11: 1,
+ 12: 1,
+ 13: 2,
+ 14: 1,
+ 15: 1,
+ 16: 1,
+ 17: 1,
+ 18: 1,
+ 19: 2,
+ 20: 1,
+ 21: 1,
+ 22: 1,
+ 23: 3,
+ 24: 2,
+ 25: 2,
+ 26: 1,
+ 27: 3,
+ 28: 2,
+ 29: 2,
+ 30: 2,
+ 31: 4,
+ 32: 9,
+ 33: 14}
+
+ Or, similarly, the maximal cliques in `G` that contain a given node.
+ For example, the 4 maximal cliques that contain node 31:
+
+ >>> [c for c in nx.find_cliques(G) if 31 in c]
+ [[0, 31], [33, 32, 31], [33, 28, 31], [24, 25, 31]]
+
+ See Also
+ --------
+ find_cliques_recursive
+ A recursive version of the same algorithm.
+
+ Notes
+ -----
+ To obtain a list of all maximal cliques, use
+ `list(find_cliques(G))`. However, be aware that in the worst-case,
+ the length of this list can be exponential in the number of nodes in
+ the graph. This function avoids storing all cliques in memory by
+ only keeping current candidate node lists in memory during its search.
+
+ This implementation is based on the algorithm published by Bron and
+ Kerbosch (1973) [1]_, as adapted by Tomita, Tanaka and Takahashi
+ (2006) [2]_ and discussed in Cazals and Karande (2008) [3]_. It
+ essentially unrolls the recursion used in the references to avoid
+ issues of recursion stack depth (for a recursive implementation, see
+ :func:`find_cliques_recursive`).
+
+ This algorithm ignores self-loops and parallel edges, since cliques
+ are not conventionally defined with such edges.
+
+ References
+ ----------
+ .. [1] Bron, C. and Kerbosch, J.
+ "Algorithm 457: finding all cliques of an undirected graph".
+ *Communications of the ACM* 16, 9 (Sep. 1973), 575--577.
+ <http://portal.acm.org/citation.cfm?doid=362342.362367>
+
+ .. [2] Etsuji Tomita, Akira Tanaka, Haruhisa Takahashi,
+ "The worst-case time complexity for generating all maximal
+ cliques and computational experiments",
+ *Theoretical Computer Science*, Volume 363, Issue 1,
+ Computing and Combinatorics,
+ 10th Annual International Conference on
+ Computing and Combinatorics (COCOON 2004), 25 October 2006, Pages 28--42
+ <https://doi.org/10.1016/j.tcs.2006.06.015>
+
+ .. [3] F. Cazals, C. Karande,
+ "A note on the problem of reporting maximal cliques",
+ *Theoretical Computer Science*,
+ Volume 407, Issues 1--3, 6 November 2008, Pages 564--568,
+ <https://doi.org/10.1016/j.tcs.2008.05.010>
+
+ """
+ if len(G) == 0:
+ return
+
+ adj = {u: {v for v in G[u] if v != u} for u in G}
+
+ # Initialize Q with the given nodes and subg, cand with their nbrs
+ Q = nodes[:] if nodes is not None else []
+ cand = set(G)
+ for node in Q:
+ if node not in cand:
+ raise ValueError(f"The given `nodes` {nodes} do not form a clique")
+ cand &= adj[node]
+
+ if not cand:
+ yield Q[:]
+ return
+
+ subg = cand.copy()
+ stack = []
+ Q.append(None)
+
+ u = max(subg, key=lambda u: len(cand & adj[u]))
+ ext_u = cand - adj[u]
+
+ try:
+ while True:
+ if ext_u:
+ q = ext_u.pop()
+ cand.remove(q)
+ Q[-1] = q
+ adj_q = adj[q]
+ subg_q = subg & adj_q
+ if not subg_q:
+ yield Q[:]
+ else:
+ cand_q = cand & adj_q
+ if cand_q:
+ stack.append((subg, cand, ext_u))
+ Q.append(None)
+ subg = subg_q
+ cand = cand_q
+ u = max(subg, key=lambda u: len(cand & adj[u]))
+ ext_u = cand - adj[u]
+ else:
+ Q.pop()
+ subg, cand, ext_u = stack.pop()
+ except IndexError:
+ pass
+
+
+# TODO Should this also be not implemented for directed graphs?
+@nx._dispatchable
+def find_cliques_recursive(G, nodes=None):
+ """Returns all maximal cliques in a graph.
+
+ For each node *v*, a *maximal clique for v* is a largest complete
+ subgraph containing *v*. The largest maximal clique is sometimes
+ called the *maximum clique*.
+
+ This function returns an iterator over cliques, each of which is a
+ list of nodes. It is a recursive implementation, so may suffer from
+ recursion depth issues, but is included for pedagogical reasons.
+ For a non-recursive implementation, see :func:`find_cliques`.
+
+ This function accepts a list of `nodes` and only the maximal cliques
+ containing all of these `nodes` are returned. It can considerably speed up
+ the running time if some specific cliques are desired.
+
+ Parameters
+ ----------
+ G : NetworkX graph
+
+ nodes : list, optional (default=None)
+ If provided, only yield *maximal cliques* containing all nodes in `nodes`.
+ If `nodes` isn't a clique itself, a ValueError is raised.
+
+ Returns
+ -------
+ iterator
+ An iterator over maximal cliques, each of which is a list of
+ nodes in `G`. If `nodes` is provided, only the maximal cliques
+ containing all the nodes in `nodes` are yielded. The order of
+ cliques is arbitrary.
+
+ Raises
+ ------
+ ValueError
+ If `nodes` is not a clique.
+
+ See Also
+ --------
+ find_cliques
+ An iterative version of the same algorithm. See docstring for examples.
+
+ Notes
+ -----
+ To obtain a list of all maximal cliques, use
+ `list(find_cliques_recursive(G))`. However, be aware that in the
+ worst-case, the length of this list can be exponential in the number
+ of nodes in the graph. This function avoids storing all cliques in memory
+ by only keeping current candidate node lists in memory during its search.
+
+ This implementation is based on the algorithm published by Bron and
+ Kerbosch (1973) [1]_, as adapted by Tomita, Tanaka and Takahashi
+ (2006) [2]_ and discussed in Cazals and Karande (2008) [3]_. For a
+ non-recursive implementation, see :func:`find_cliques`.
+
+ This algorithm ignores self-loops and parallel edges, since cliques
+ are not conventionally defined with such edges.
+
+ References
+ ----------
+ .. [1] Bron, C. and Kerbosch, J.
+ "Algorithm 457: finding all cliques of an undirected graph".
+ *Communications of the ACM* 16, 9 (Sep. 1973), 575--577.
+ <http://portal.acm.org/citation.cfm?doid=362342.362367>
+
+ .. [2] Etsuji Tomita, Akira Tanaka, Haruhisa Takahashi,
+ "The worst-case time complexity for generating all maximal
+ cliques and computational experiments",
+ *Theoretical Computer Science*, Volume 363, Issue 1,
+ Computing and Combinatorics,
+ 10th Annual International Conference on
+ Computing and Combinatorics (COCOON 2004), 25 October 2006, Pages 28--42
+ <https://doi.org/10.1016/j.tcs.2006.06.015>
+
+ .. [3] F. Cazals, C. Karande,
+ "A note on the problem of reporting maximal cliques",
+ *Theoretical Computer Science*,
+ Volume 407, Issues 1--3, 6 November 2008, Pages 564--568,
+ <https://doi.org/10.1016/j.tcs.2008.05.010>
+
+ """
+ if len(G) == 0:
+ return iter([])
+
+ adj = {u: {v for v in G[u] if v != u} for u in G}
+
+ # Initialize Q with the given nodes and subg, cand with their nbrs
+ Q = nodes[:] if nodes is not None else []
+ cand_init = set(G)
+ for node in Q:
+ if node not in cand_init:
+ raise ValueError(f"The given `nodes` {nodes} do not form a clique")
+ cand_init &= adj[node]
+
+ if not cand_init:
+ return iter([Q])
+
+ subg_init = cand_init.copy()
+
+ def expand(subg, cand):
+ u = max(subg, key=lambda u: len(cand & adj[u]))
+ for q in cand - adj[u]:
+ cand.remove(q)
+ Q.append(q)
+ adj_q = adj[q]
+ subg_q = subg & adj_q
+ if not subg_q:
+ yield Q[:]
+ else:
+ cand_q = cand & adj_q
+ if cand_q:
+ yield from expand(subg_q, cand_q)
+ Q.pop()
+
+ return expand(subg_init, cand_init)
+
+
+@nx._dispatchable(returns_graph=True)
+def make_max_clique_graph(G, create_using=None):
+ """Returns the maximal clique graph of the given graph.
+
+ The nodes of the maximal clique graph of `G` are the cliques of
+ `G` and an edge joins two cliques if the cliques are not disjoint.
+
+ Parameters
+ ----------
+ G : NetworkX graph
+
+ create_using : NetworkX graph constructor, optional (default=nx.Graph)
+ Graph type to create. If graph instance, then cleared before populated.
+
+ Returns
+ -------
+ NetworkX graph
+ A graph whose nodes are the cliques of `G` and whose edges
+ join two cliques if they are not disjoint.
+
+ Notes
+ -----
+ This function behaves like the following code::
+
+ import networkx as nx
+
+ G = nx.make_clique_bipartite(G)
+ cliques = [v for v in G.nodes() if G.nodes[v]["bipartite"] == 0]
+ G = nx.bipartite.projected_graph(G, cliques)
+ G = nx.relabel_nodes(G, {-v: v - 1 for v in G})
+
+ It should be faster, though, since it skips all the intermediate
+ steps.
+
+ """
+ if create_using is None:
+ B = G.__class__()
+ else:
+ B = nx.empty_graph(0, create_using)
+ cliques = list(enumerate(set(c) for c in find_cliques(G)))
+ # Add a numbered node for each clique.
+ B.add_nodes_from(i for i, c in cliques)
+ # Join cliques by an edge if they share a node.
+ clique_pairs = combinations(cliques, 2)
+ B.add_edges_from((i, j) for (i, c1), (j, c2) in clique_pairs if c1 & c2)
+ return B
+
+
+@nx._dispatchable(returns_graph=True)
+def make_clique_bipartite(G, fpos=None, create_using=None, name=None):
+ """Returns the bipartite clique graph corresponding to `G`.
+
+ In the returned bipartite graph, the "bottom" nodes are the nodes of
+ `G` and the "top" nodes represent the maximal cliques of `G`.
+ There is an edge from node *v* to clique *C* in the returned graph
+ if and only if *v* is an element of *C*.
+
+ Parameters
+ ----------
+ G : NetworkX graph
+ An undirected graph.
+
+ fpos : bool
+ If True or not None, the returned graph will have an
+ additional attribute, `pos`, a dictionary mapping node to
+ position in the Euclidean plane.
+
+ create_using : NetworkX graph constructor, optional (default=nx.Graph)
+ Graph type to create. If graph instance, then cleared before populated.
+
+ Returns
+ -------
+ NetworkX graph
+ A bipartite graph whose "bottom" set is the nodes of the graph
+ `G`, whose "top" set is the cliques of `G`, and whose edges
+ join nodes of `G` to the cliques that contain them.
+
+ The nodes of the graph `G` have the node attribute
+ 'bipartite' set to 1 and the nodes representing cliques
+ have the node attribute 'bipartite' set to 0, as is the
+ convention for bipartite graphs in NetworkX.
+
+ """
+ B = nx.empty_graph(0, create_using)
+ B.clear()
+ # The "bottom" nodes in the bipartite graph are the nodes of the
+ # original graph, G.
+ B.add_nodes_from(G, bipartite=1)
+ for i, cl in enumerate(find_cliques(G)):
+ # The "top" nodes in the bipartite graph are the cliques. These
+ # nodes get negative numbers as labels.
+ name = -i - 1
+ B.add_node(name, bipartite=0)
+ B.add_edges_from((v, name) for v in cl)
+ return B
+
+
+@nx._dispatchable
+def node_clique_number(G, nodes=None, cliques=None, separate_nodes=False):
+ """Returns the size of the largest maximal clique containing each given node.
+
+ Returns a single or list depending on input nodes.
+ An optional list of cliques can be input if already computed.
+
+ Parameters
+ ----------
+ G : NetworkX graph
+ An undirected graph.
+
+ cliques : list, optional (default=None)
+ A list of cliques, each of which is itself a list of nodes.
+ If not specified, the list of all cliques will be computed
+ using :func:`find_cliques`.
+
+ Returns
+ -------
+ int or dict
+ If `nodes` is a single node, returns the size of the
+ largest maximal clique in `G` containing that node.
+ Otherwise return a dict keyed by node to the size
+ of the largest maximal clique containing that node.
+
+ See Also
+ --------
+ find_cliques
+ find_cliques yields the maximal cliques of G.
+ It accepts a `nodes` argument which restricts consideration to
+ maximal cliques containing all the given `nodes`.
+ The search for the cliques is optimized for `nodes`.
+ """
+ if cliques is None:
+ if nodes is not None:
+ # Use ego_graph to decrease size of graph
+ # check for single node
+ if nodes in G:
+ return max(len(c) for c in find_cliques(nx.ego_graph(G, nodes)))
+ # handle multiple nodes
+ return {
+ n: max(len(c) for c in find_cliques(nx.ego_graph(G, n))) for n in nodes
+ }
+
+ # nodes is None--find all cliques
+ cliques = list(find_cliques(G))
+
+ # single node requested
+ if nodes in G:
+ return max(len(c) for c in cliques if nodes in c)
+
+ # multiple nodes requested
+ # preprocess all nodes (faster than one at a time for even 2 nodes)
+ size_for_n = defaultdict(int)
+ for c in cliques:
+ size_of_c = len(c)
+ for n in c:
+ if size_for_n[n] < size_of_c:
+ size_for_n[n] = size_of_c
+ if nodes is None:
+ return size_for_n
+ return {n: size_for_n[n] for n in nodes}
+
+
+def number_of_cliques(G, nodes=None, cliques=None):
+ """Returns the number of maximal cliques for each node.
+
+ Returns a single or list depending on input nodes.
+ Optional list of cliques can be input if already computed.
+ """
+ if cliques is None:
+ cliques = list(find_cliques(G))
+
+ if nodes is None:
+ nodes = list(G.nodes()) # none, get entire graph
+
+ if not isinstance(nodes, list): # check for a list
+ v = nodes
+ # assume it is a single value
+ numcliq = len([1 for c in cliques if v in c])
+ else:
+ numcliq = {}
+ for v in nodes:
+ numcliq[v] = len([1 for c in cliques if v in c])
+ return numcliq
+
+
+class MaxWeightClique:
+ """A class for the maximum weight clique algorithm.
+
+ This class is a helper for the `max_weight_clique` function. The class
+ should not normally be used directly.
+
+ Parameters
+ ----------
+ G : NetworkX graph
+ The undirected graph for which a maximum weight clique is sought
+ weight : string or None, optional (default='weight')
+ The node attribute that holds the integer value used as a weight.
+ If None, then each node has weight 1.
+
+ Attributes
+ ----------
+ G : NetworkX graph
+ The undirected graph for which a maximum weight clique is sought
+ node_weights: dict
+ The weight of each node
+ incumbent_nodes : list
+ The nodes of the incumbent clique (the best clique found so far)
+ incumbent_weight: int
+ The weight of the incumbent clique
+ """
+
+ def __init__(self, G, weight):
+ self.G = G
+ self.incumbent_nodes = []
+ self.incumbent_weight = 0
+
+ if weight is None:
+ self.node_weights = {v: 1 for v in G.nodes()}
+ else:
+ for v in G.nodes():
+ if weight not in G.nodes[v]:
+ errmsg = f"Node {v!r} does not have the requested weight field."
+ raise KeyError(errmsg)
+ if not isinstance(G.nodes[v][weight], int):
+ errmsg = f"The {weight!r} field of node {v!r} is not an integer."
+ raise ValueError(errmsg)
+ self.node_weights = {v: G.nodes[v][weight] for v in G.nodes()}
+
+ def update_incumbent_if_improved(self, C, C_weight):
+ """Update the incumbent if the node set C has greater weight.
+
+ C is assumed to be a clique.
+ """
+ if C_weight > self.incumbent_weight:
+ self.incumbent_nodes = C[:]
+ self.incumbent_weight = C_weight
+
+ def greedily_find_independent_set(self, P):
+ """Greedily find an independent set of nodes from a set of
+ nodes P."""
+ independent_set = []
+ P = P[:]
+ while P:
+ v = P[0]
+ independent_set.append(v)
+ P = [w for w in P if v != w and not self.G.has_edge(v, w)]
+ return independent_set
+
+ def find_branching_nodes(self, P, target):
+ """Find a set of nodes to branch on."""
+ residual_wt = {v: self.node_weights[v] for v in P}
+ total_wt = 0
+ P = P[:]
+ while P:
+ independent_set = self.greedily_find_independent_set(P)
+ min_wt_in_class = min(residual_wt[v] for v in independent_set)
+ total_wt += min_wt_in_class
+ if total_wt > target:
+ break
+ for v in independent_set:
+ residual_wt[v] -= min_wt_in_class
+ P = [v for v in P if residual_wt[v] != 0]
+ return P
+
+ def expand(self, C, C_weight, P):
+ """Look for the best clique that contains all the nodes in C and zero or
+ more of the nodes in P, backtracking if it can be shown that no such
+ clique has greater weight than the incumbent.
+ """
+ self.update_incumbent_if_improved(C, C_weight)
+ branching_nodes = self.find_branching_nodes(P, self.incumbent_weight - C_weight)
+ while branching_nodes:
+ v = branching_nodes.pop()
+ P.remove(v)
+ new_C = C + [v]
+ new_C_weight = C_weight + self.node_weights[v]
+ new_P = [w for w in P if self.G.has_edge(v, w)]
+ self.expand(new_C, new_C_weight, new_P)
+
+ def find_max_weight_clique(self):
+ """Find a maximum weight clique."""
+ # Sort nodes in reverse order of degree for speed
+ nodes = sorted(self.G.nodes(), key=lambda v: self.G.degree(v), reverse=True)
+ nodes = [v for v in nodes if self.node_weights[v] > 0]
+ self.expand([], 0, nodes)
+
+
+@not_implemented_for("directed")
+@nx._dispatchable(node_attrs="weight")
+def max_weight_clique(G, weight="weight"):
+ """Find a maximum weight clique in G.
+
+ A *clique* in a graph is a set of nodes such that every two distinct nodes
+ are adjacent. The *weight* of a clique is the sum of the weights of its
+ nodes. A *maximum weight clique* of graph G is a clique C in G such that
+ no clique in G has weight greater than the weight of C.
+
+ Parameters
+ ----------
+ G : NetworkX graph
+ Undirected graph
+ weight : string or None, optional (default='weight')
+ The node attribute that holds the integer value used as a weight.
+ If None, then each node has weight 1.
+
+ Returns
+ -------
+ clique : list
+ the nodes of a maximum weight clique
+ weight : int
+ the weight of a maximum weight clique
+
+ Notes
+ -----
+ The implementation is recursive, and therefore it may run into recursion
+ depth issues if G contains a clique whose number of nodes is close to the
+ recursion depth limit.
+
+ At each search node, the algorithm greedily constructs a weighted
+ independent set cover of part of the graph in order to find a small set of
+ nodes on which to branch. The algorithm is very similar to the algorithm
+ of Tavares et al. [1]_, other than the fact that the NetworkX version does
+ not use bitsets. This style of algorithm for maximum weight clique (and
+ maximum weight independent set, which is the same problem but on the
+ complement graph) has a decades-long history. See Algorithm B of Warren
+ and Hicks [2]_ and the references in that paper.
+
+ References
+ ----------
+ .. [1] Tavares, W.A., Neto, M.B.C., Rodrigues, C.D., Michelon, P.: Um
+ algoritmo de branch and bound para o problema da clique máxima
+ ponderada. Proceedings of XLVII SBPO 1 (2015).
+
+ .. [2] Warren, Jeffrey S, Hicks, Illya V.: Combinatorial Branch-and-Bound
+ for the Maximum Weight Independent Set Problem. Technical Report,
+ Texas A&M University (2016).
+ """
+
+ mwc = MaxWeightClique(G, weight)
+ mwc.find_max_weight_clique()
+ return mwc.incumbent_nodes, mwc.incumbent_weight