Repository: networkx/networkx

Issue:
Deprecate `nx.number_of_cliques`?
Just a thought I had while [reviewing 6171](https://github.com/networkx/networkx/pull/6171#pullrequestreview-1169036037).

The `number_of_cliques` helper function creates (by default) a dictionary keyed by node where the values are the number of cliques containing the node. The default behavior essentially boils down to a one-liner:

```python
{n: sum(1 for c in nx.find_cliques(G) if n in c) for n in G}
```

The majority of the implementation of `nx.number_of_cliques` is dedicated to munging the various input arguments. IMO it'd be an improvement to instead recommend that users do this computation the Pythonic way rather than provide networkx-specific API for this task. IMO the potential upside for future users is worth the noise of a deprecation, but this is definitely subjective! Thoughts?


Discussion:
Another function like that is `graph_number_of_cliques`.  It is literally `len(cliques)` if the input `cliques` is already computed and it is `sum(1 for c in nx.find_cliques(G))` if the cliques input is `None`. Removing these functions (I am sure there are others in this module) and replacing them with examples in the `find_cliques` doc_string seems like a good idea.
Looking over all the functions in the `clique.py` module, the ones that could be replaced by one-liners on the output of `find_cliques` are:
 - `graph_clique_number`: returns the size of the largest clique in the graph
 - `graph_number_of_cliques`: returns the number of maximal cliques in the graph
 - `number_of_cliques`: The number of maximal cliques for each node
 - `cliques_containing_node`: List of maximal cliques for each node

Resolving patch:
diff --git a/networkx/algorithms/clique.py b/networkx/algorithms/clique.py
--- a/networkx/algorithms/clique.py
+++ b/networkx/algorithms/clique.py
@@ -137,6 +137,67 @@ def find_cliques(G, nodes=None):
     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
@@ -274,7 +335,7 @@ def find_cliques_recursive(G, nodes=None):
     See Also
     --------
     find_cliques
-        An iterative version of the same algorithm.
+        An iterative version of the same algorithm. See docstring for examples.
 
     Notes
     -----
@@ -451,6 +512,14 @@ def graph_clique_number(G, cliques=None):
     The *clique number* of a graph is the size of the largest clique in
     the graph.
 
+    .. deprecated:: 3.0
+
+       graph_clique_number is deprecated in NetworkX 3.0 and will be removed
+       in v3.2. The graph clique number can be computed directly with::
+
+           max(len(c) for c in nx.find_cliques(G))
+
+
     Parameters
     ----------
     G : NetworkX graph
@@ -473,6 +542,16 @@ def graph_clique_number(G, cliques=None):
     maximal cliques.
 
     """
+    import warnings
+
+    warnings.warn(
+        (
+            "\n\ngraph_clique_number is deprecated and will be removed.\n"
+            "Use: ``max(len(c) for c in nx.find_cliques(G))`` instead."
+        ),
+        DeprecationWarning,
+        stacklevel=2,
+    )
     if len(G.nodes) < 1:
         return 0
     if cliques is None:
@@ -483,6 +562,13 @@ def graph_clique_number(G, cliques=None):
 def graph_number_of_cliques(G, cliques=None):
     """Returns the number of maximal cliques in the graph.
 
+    .. deprecated:: 3.0
+
+       graph_number_of_cliques is deprecated and will be removed in v3.2.
+       The number of maximal cliques can be computed directly with::
+
+           sum(1 for _ in nx.find_cliques(G))
+
     Parameters
     ----------
     G : NetworkX graph
@@ -505,6 +591,16 @@ def graph_number_of_cliques(G, cliques=None):
     maximal cliques.
 
     """
+    import warnings
+
+    warnings.warn(
+        (
+            "\n\ngraph_number_of_cliques is deprecated and will be removed.\n"
+            "Use: ``sum(1 for _ in nx.find_cliques(G))`` instead."
+        ),
+        DeprecationWarning,
+        stacklevel=2,
+    )
     if cliques is None:
         cliques = list(find_cliques(G))
     return len(cliques)
@@ -576,9 +672,29 @@ def node_clique_number(G, nodes=None, cliques=None, separate_nodes=False):
 def number_of_cliques(G, nodes=None, cliques=None):
     """Returns the number of maximal cliques for each node.
 
+    .. deprecated:: 3.0
+
+       number_of_cliques is deprecated and will be removed in v3.2.
+       Use the result of `find_cliques` directly to compute the number of
+       cliques containing each node::
+
+           {n: sum(1 for c in nx.find_cliques(G) if n in c) for n in G}
+
     Returns a single or list depending on input nodes.
     Optional list of cliques can be input if already computed.
     """
+    import warnings
+
+    warnings.warn(
+        (
+            "\n\nnumber_of_cliques is deprecated and will be removed.\n"
+            "Use the result of find_cliques directly to compute the number\n"
+            "of cliques containing each node:\n\n"
+            "    {n: sum(1 for c in nx.find_cliques(G) if n in c) for n in G}\n\n"
+        ),
+        DeprecationWarning,
+        stacklevel=2,
+    )
     if cliques is None:
         cliques = list(find_cliques(G))
 
@@ -599,9 +715,29 @@ def number_of_cliques(G, nodes=None, cliques=None):
 def cliques_containing_node(G, nodes=None, cliques=None):
     """Returns a list of cliques containing the given node.
 
+    .. deprecated:: 3.0
+
+       cliques_containing_node is deprecated and will be removed in 3.2.
+       Use the result of `find_cliques` directly to compute the cliques that
+       contain each node::
+
+           {n: [c for c in nx.find_cliques(G) if n in c] for n in G}
+
     Returns a single list or list of lists depending on input nodes.
     Optional list of cliques can be input if already computed.
     """
+    import warnings
+
+    warnings.warn(
+        (
+            "\n\ncliques_containing_node is deprecated and will be removed.\n"
+            "Use the result of find_cliques directly to compute maximal cliques\n"
+            "containing each node:\n\n"
+            "    {n: [c for c in nx.find_cliques(G) if n in c] for n in G}\n\n"
+        ),
+        DeprecationWarning,
+        stacklevel=2,
+    )
     if cliques is None:
         cliques = list(find_cliques(G))
 
diff --git a/networkx/algorithms/isomorphism/isomorph.py b/networkx/algorithms/isomorphism/isomorph.py
--- a/networkx/algorithms/isomorphism/isomorph.py
+++ b/networkx/algorithms/isomorphism/isomorph.py
@@ -33,13 +33,15 @@ def could_be_isomorphic(G1, G2):
     # Check local properties
     d1 = G1.degree()
     t1 = nx.triangles(G1)
-    c1 = nx.number_of_cliques(G1)
+    clqs_1 = list(nx.find_cliques(G1))
+    c1 = {n: sum(1 for c in clqs_1 if n in c) for n in G1}  # number of cliques
     props1 = [[d, t1[v], c1[v]] for v, d in d1]
     props1.sort()
 
     d2 = G2.degree()
     t2 = nx.triangles(G2)
-    c2 = nx.number_of_cliques(G2)
+    clqs_2 = list(nx.find_cliques(G2))
+    c2 = {n: sum(1 for c in clqs_2 if n in c) for n in G2}  # number of cliques
     props2 = [[d, t2[v], c2[v]] for v, d in d2]
     props2.sort()
 


Test patch:
diff --git a/networkx/algorithms/tests/test_clique.py b/networkx/algorithms/tests/test_clique.py
--- a/networkx/algorithms/tests/test_clique.py
+++ b/networkx/algorithms/tests/test_clique.py
@@ -69,80 +69,96 @@ def test_find_cliques3(self):
 
     def test_clique_number(self):
         G = self.G
-        assert nx.graph_clique_number(G) == 4
-        assert nx.graph_clique_number(G, cliques=self.cl) == 4
+        with pytest.deprecated_call():
+            assert nx.graph_clique_number(G) == 4
+        with pytest.deprecated_call():
+            assert nx.graph_clique_number(G, cliques=self.cl) == 4
 
     def test_clique_number2(self):
         G = nx.Graph()
         G.add_nodes_from([1, 2, 3])
-        assert nx.graph_clique_number(G) == 1
+        with pytest.deprecated_call():
+            assert nx.graph_clique_number(G) == 1
 
     def test_clique_number3(self):
         G = nx.Graph()
-        assert nx.graph_clique_number(G) == 0
+        with pytest.deprecated_call():
+            assert nx.graph_clique_number(G) == 0
 
     def test_number_of_cliques(self):
         G = self.G
-        assert nx.graph_number_of_cliques(G) == 5
-        assert nx.graph_number_of_cliques(G, cliques=self.cl) == 5
-        assert nx.number_of_cliques(G, 1) == 1
-        assert list(nx.number_of_cliques(G, [1]).values()) == [1]
-        assert list(nx.number_of_cliques(G, [1, 2]).values()) == [1, 2]
-        assert nx.number_of_cliques(G, [1, 2]) == {1: 1, 2: 2}
-        assert nx.number_of_cliques(G, 2) == 2
-        assert nx.number_of_cliques(G) == {
-            1: 1,
-            2: 2,
-            3: 1,
-            4: 2,
-            5: 1,
-            6: 2,
-            7: 1,
-            8: 1,
-            9: 1,
-            10: 1,
-            11: 1,
-        }
-        assert nx.number_of_cliques(G, nodes=list(G)) == {
-            1: 1,
-            2: 2,
-            3: 1,
-            4: 2,
-            5: 1,
-            6: 2,
-            7: 1,
-            8: 1,
-            9: 1,
-            10: 1,
-            11: 1,
-        }
-        assert nx.number_of_cliques(G, nodes=[2, 3, 4]) == {2: 2, 3: 1, 4: 2}
-        assert nx.number_of_cliques(G, cliques=self.cl) == {
-            1: 1,
-            2: 2,
-            3: 1,
-            4: 2,
-            5: 1,
-            6: 2,
-            7: 1,
-            8: 1,
-            9: 1,
-            10: 1,
-            11: 1,
-        }
-        assert nx.number_of_cliques(G, list(G), cliques=self.cl) == {
-            1: 1,
-            2: 2,
-            3: 1,
-            4: 2,
-            5: 1,
-            6: 2,
-            7: 1,
-            8: 1,
-            9: 1,
-            10: 1,
-            11: 1,
-        }
+        with pytest.deprecated_call():
+            assert nx.graph_number_of_cliques(G) == 5
+        with pytest.deprecated_call():
+            assert nx.graph_number_of_cliques(G, cliques=self.cl) == 5
+        with pytest.deprecated_call():
+            assert nx.number_of_cliques(G, 1) == 1
+        with pytest.deprecated_call():
+            assert list(nx.number_of_cliques(G, [1]).values()) == [1]
+        with pytest.deprecated_call():
+            assert list(nx.number_of_cliques(G, [1, 2]).values()) == [1, 2]
+        with pytest.deprecated_call():
+            assert nx.number_of_cliques(G, [1, 2]) == {1: 1, 2: 2}
+        with pytest.deprecated_call():
+            assert nx.number_of_cliques(G, 2) == 2
+        with pytest.deprecated_call():
+            assert nx.number_of_cliques(G) == {
+                1: 1,
+                2: 2,
+                3: 1,
+                4: 2,
+                5: 1,
+                6: 2,
+                7: 1,
+                8: 1,
+                9: 1,
+                10: 1,
+                11: 1,
+            }
+        with pytest.deprecated_call():
+            assert nx.number_of_cliques(G, nodes=list(G)) == {
+                1: 1,
+                2: 2,
+                3: 1,
+                4: 2,
+                5: 1,
+                6: 2,
+                7: 1,
+                8: 1,
+                9: 1,
+                10: 1,
+                11: 1,
+            }
+        with pytest.deprecated_call():
+            assert nx.number_of_cliques(G, nodes=[2, 3, 4]) == {2: 2, 3: 1, 4: 2}
+        with pytest.deprecated_call():
+            assert nx.number_of_cliques(G, cliques=self.cl) == {
+                1: 1,
+                2: 2,
+                3: 1,
+                4: 2,
+                5: 1,
+                6: 2,
+                7: 1,
+                8: 1,
+                9: 1,
+                10: 1,
+                11: 1,
+            }
+        with pytest.deprecated_call():
+            assert nx.number_of_cliques(G, list(G), cliques=self.cl) == {
+                1: 1,
+                2: 2,
+                3: 1,
+                4: 2,
+                5: 1,
+                6: 2,
+                7: 1,
+                8: 1,
+                9: 1,
+                10: 1,
+                11: 1,
+            }
 
     def test_node_clique_number(self):
         G = self.G
@@ -182,23 +198,31 @@ def test_node_clique_number(self):
 
     def test_cliques_containing_node(self):
         G = self.G
-        assert nx.cliques_containing_node(G, 1) == [[2, 6, 1, 3]]
-        assert list(nx.cliques_containing_node(G, [1]).values()) == [[[2, 6, 1, 3]]]
-        assert [
-            sorted(c) for c in list(nx.cliques_containing_node(G, [1, 2]).values())
-        ] == [[[2, 6, 1, 3]], [[2, 6, 1, 3], [2, 6, 4]]]
-        result = nx.cliques_containing_node(G, [1, 2])
+        with pytest.deprecated_call():
+            assert nx.cliques_containing_node(G, 1) == [[2, 6, 1, 3]]
+        with pytest.deprecated_call():
+            assert list(nx.cliques_containing_node(G, [1]).values()) == [[[2, 6, 1, 3]]]
+        with pytest.deprecated_call():
+            assert [
+                sorted(c) for c in list(nx.cliques_containing_node(G, [1, 2]).values())
+            ] == [[[2, 6, 1, 3]], [[2, 6, 1, 3], [2, 6, 4]]]
+        with pytest.deprecated_call():
+            result = nx.cliques_containing_node(G, [1, 2])
         for k, v in result.items():
             result[k] = sorted(v)
         assert result == {1: [[2, 6, 1, 3]], 2: [[2, 6, 1, 3], [2, 6, 4]]}
-        assert nx.cliques_containing_node(G, 1) == [[2, 6, 1, 3]]
+        with pytest.deprecated_call():
+            assert nx.cliques_containing_node(G, 1) == [[2, 6, 1, 3]]
         expected = [{2, 6, 1, 3}, {2, 6, 4}]
-        answer = [set(c) for c in nx.cliques_containing_node(G, 2)]
+        with pytest.deprecated_call():
+            answer = [set(c) for c in nx.cliques_containing_node(G, 2)]
         assert answer in (expected, list(reversed(expected)))
 
-        answer = [set(c) for c in nx.cliques_containing_node(G, 2, cliques=self.cl)]
+        with pytest.deprecated_call():
+            answer = [set(c) for c in nx.cliques_containing_node(G, 2, cliques=self.cl)]
         assert answer in (expected, list(reversed(expected)))
-        assert len(nx.cliques_containing_node(G)) == 11
+        with pytest.deprecated_call():
+            assert len(nx.cliques_containing_node(G)) == 11
 
     def test_make_clique_bipartite(self):
         G = self.G