1"""Provides algorithms supporting the computation of graph polynomials.
2
3Graph polynomials are polynomial-valued graph invariants that encode a wide
4variety of structural information. Examples include the Tutte polynomial,
5chromatic polynomial, characteristic polynomial, and matching polynomial. An
6extensive treatment is provided in [1]_.
7
8For a simple example, the `~sympy.matrices.matrices.MatrixDeterminant.charpoly`
9method can be used to compute the characteristic polynomial from the adjacency
10matrix of a graph. Consider the complete graph ``K_4``:
11
12>>> import sympy
13>>> x = sympy.Symbol("x")
14>>> G = nx.complete_graph(4)
15>>> A = nx.to_numpy_array(G, dtype=int)
16>>> M = sympy.SparseMatrix(A)
17>>> M.charpoly(x).as_expr()
18x**4 - 6*x**2 - 8*x - 3
19
20
21.. [1] Y. Shi, M. Dehmer, X. Li, I. Gutman,
22 "Graph Polynomials"
23"""
24
25from collections import deque
26
27import networkx as nx
28from networkx.utils import not_implemented_for
29
30__all__ = ["tutte_polynomial", "chromatic_polynomial"]
31
32
33@not_implemented_for("directed")
34@nx._dispatchable
35def tutte_polynomial(G):
36 r"""Returns the Tutte polynomial of `G`
37
38 This function computes the Tutte polynomial via an iterative version of
39 the deletion-contraction algorithm.
40
41 The Tutte polynomial `T_G(x, y)` is a fundamental graph polynomial invariant in
42 two variables. It encodes a wide array of information related to the
43 edge-connectivity of a graph; "Many problems about graphs can be reduced to
44 problems of finding and evaluating the Tutte polynomial at certain values" [1]_.
45 In fact, every deletion-contraction-expressible feature of a graph is a
46 specialization of the Tutte polynomial [2]_ (see Notes for examples).
47
48 There are several equivalent definitions; here are three:
49
50 Def 1 (rank-nullity expansion): For `G` an undirected graph, `n(G)` the
51 number of vertices of `G`, `E` the edge set of `G`, `V` the vertex set of
52 `G`, and `c(A)` the number of connected components of the graph with vertex
53 set `V` and edge set `A` [3]_:
54
55 .. math::
56
57 T_G(x, y) = \sum_{A \in E} (x-1)^{c(A) - c(E)} (y-1)^{c(A) + |A| - n(G)}
58
59 Def 2 (spanning tree expansion): Let `G` be an undirected graph, `T` a spanning
60 tree of `G`, and `E` the edge set of `G`. Let `E` have an arbitrary strict
61 linear order `L`. Let `B_e` be the unique minimal nonempty edge cut of
62 $E \setminus T \cup {e}$. An edge `e` is internally active with respect to
63 `T` and `L` if `e` is the least edge in `B_e` according to the linear order
64 `L`. The internal activity of `T` (denoted `i(T)`) is the number of edges
65 in $E \setminus T$ that are internally active with respect to `T` and `L`.
66 Let `P_e` be the unique path in $T \cup {e}$ whose source and target vertex
67 are the same. An edge `e` is externally active with respect to `T` and `L`
68 if `e` is the least edge in `P_e` according to the linear order `L`. The
69 external activity of `T` (denoted `e(T)`) is the number of edges in
70 $E \setminus T$ that are externally active with respect to `T` and `L`.
71 Then [4]_ [5]_:
72
73 .. math::
74
75 T_G(x, y) = \sum_{T \text{ a spanning tree of } G} x^{i(T)} y^{e(T)}
76
77 Def 3 (deletion-contraction recurrence): For `G` an undirected graph, `G-e`
78 the graph obtained from `G` by deleting edge `e`, `G/e` the graph obtained
79 from `G` by contracting edge `e`, `k(G)` the number of cut-edges of `G`,
80 and `l(G)` the number of self-loops of `G`:
81
82 .. math::
83 T_G(x, y) = \begin{cases}
84 x^{k(G)} y^{l(G)}, & \text{if all edges are cut-edges or self-loops} \\
85 T_{G-e}(x, y) + T_{G/e}(x, y), & \text{otherwise, for an arbitrary edge $e$ not a cut-edge or loop}
86 \end{cases}
87
88 Parameters
89 ----------
90 G : NetworkX graph
91
92 Returns
93 -------
94 instance of `sympy.core.add.Add`
95 A Sympy expression representing the Tutte polynomial for `G`.
96
97 Examples
98 --------
99 >>> C = nx.cycle_graph(5)
100 >>> nx.tutte_polynomial(C)
101 x**4 + x**3 + x**2 + x + y
102
103 >>> D = nx.diamond_graph()
104 >>> nx.tutte_polynomial(D)
105 x**3 + 2*x**2 + 2*x*y + x + y**2 + y
106
107 Notes
108 -----
109 Some specializations of the Tutte polynomial:
110
111 - `T_G(1, 1)` counts the number of spanning trees of `G`
112 - `T_G(1, 2)` counts the number of connected spanning subgraphs of `G`
113 - `T_G(2, 1)` counts the number of spanning forests in `G`
114 - `T_G(0, 2)` counts the number of strong orientations of `G`
115 - `T_G(2, 0)` counts the number of acyclic orientations of `G`
116
117 Edge contraction is defined and deletion-contraction is introduced in [6]_.
118 Combinatorial meaning of the coefficients is introduced in [7]_.
119 Universality, properties, and applications are discussed in [8]_.
120
121 Practically, up-front computation of the Tutte polynomial may be useful when
122 users wish to repeatedly calculate edge-connectivity-related information
123 about one or more graphs.
124
125 References
126 ----------
127 .. [1] M. Brandt,
128 "The Tutte Polynomial."
129 Talking About Combinatorial Objects Seminar, 2015
130 https://math.berkeley.edu/~brandtm/talks/tutte.pdf
131 .. [2] A. Björklund, T. Husfeldt, P. Kaski, M. Koivisto,
132 "Computing the Tutte polynomial in vertex-exponential time"
133 49th Annual IEEE Symposium on Foundations of Computer Science, 2008
134 https://ieeexplore.ieee.org/abstract/document/4691000
135 .. [3] Y. Shi, M. Dehmer, X. Li, I. Gutman,
136 "Graph Polynomials," p. 14
137 .. [4] Y. Shi, M. Dehmer, X. Li, I. Gutman,
138 "Graph Polynomials," p. 46
139 .. [5] A. Nešetril, J. Goodall,
140 "Graph invariants, homomorphisms, and the Tutte polynomial"
141 https://iuuk.mff.cuni.cz/~andrew/Tutte.pdf
142 .. [6] D. B. West,
143 "Introduction to Graph Theory," p. 84
144 .. [7] G. Coutinho,
145 "A brief introduction to the Tutte polynomial"
146 Structural Analysis of Complex Networks, 2011
147 https://homepages.dcc.ufmg.br/~gabriel/seminars/coutinho_tuttepolynomial_seminar.pdf
148 .. [8] J. A. Ellis-Monaghan, C. Merino,
149 "Graph polynomials and their applications I: The Tutte polynomial"
150 Structural Analysis of Complex Networks, 2011
151 https://arxiv.org/pdf/0803.3079.pdf
152 """
153 import sympy
154
155 x = sympy.Symbol("x")
156 y = sympy.Symbol("y")
157 stack = deque()
158 stack.append(nx.MultiGraph(G))
159
160 polynomial = 0
161 while stack:
162 G = stack.pop()
163 bridges = set(nx.bridges(G))
164
165 e = None
166 for i in G.edges:
167 if (i[0], i[1]) not in bridges and i[0] != i[1]:
168 e = i
169 break
170 if not e:
171 loops = list(nx.selfloop_edges(G, keys=True))
172 polynomial += x ** len(bridges) * y ** len(loops)
173 else:
174 # deletion-contraction
175 C = nx.contracted_edge(G, e, self_loops=True)
176 C.remove_edge(e[0], e[0])
177 G.remove_edge(*e)
178 stack.append(G)
179 stack.append(C)
180 return sympy.simplify(polynomial)
181
182
183@not_implemented_for("directed")
184@nx._dispatchable
185def chromatic_polynomial(G):
186 r"""Returns the chromatic polynomial of `G`
187
188 This function computes the chromatic polynomial via an iterative version of
189 the deletion-contraction algorithm.
190
191 The chromatic polynomial `X_G(x)` is a fundamental graph polynomial
192 invariant in one variable. Evaluating `X_G(k)` for an natural number `k`
193 enumerates the proper k-colorings of `G`.
194
195 There are several equivalent definitions; here are three:
196
197 Def 1 (explicit formula):
198 For `G` an undirected graph, `c(G)` the number of connected components of
199 `G`, `E` the edge set of `G`, and `G(S)` the spanning subgraph of `G` with
200 edge set `S` [1]_:
201
202 .. math::
203
204 X_G(x) = \sum_{S \subseteq E} (-1)^{|S|} x^{c(G(S))}
205
206
207 Def 2 (interpolating polynomial):
208 For `G` an undirected graph, `n(G)` the number of vertices of `G`, `k_0 = 0`,
209 and `k_i` the number of distinct ways to color the vertices of `G` with `i`
210 unique colors (for `i` a natural number at most `n(G)`), `X_G(x)` is the
211 unique Lagrange interpolating polynomial of degree `n(G)` through the points
212 `(0, k_0), (1, k_1), \dots, (n(G), k_{n(G)})` [2]_.
213
214
215 Def 3 (chromatic recurrence):
216 For `G` an undirected graph, `G-e` the graph obtained from `G` by deleting
217 edge `e`, `G/e` the graph obtained from `G` by contracting edge `e`, `n(G)`
218 the number of vertices of `G`, and `e(G)` the number of edges of `G` [3]_:
219
220 .. math::
221 X_G(x) = \begin{cases}
222 x^{n(G)}, & \text{if $e(G)=0$} \\
223 X_{G-e}(x) - X_{G/e}(x), & \text{otherwise, for an arbitrary edge $e$}
224 \end{cases}
225
226 This formulation is also known as the Fundamental Reduction Theorem [4]_.
227
228
229 Parameters
230 ----------
231 G : NetworkX graph
232
233 Returns
234 -------
235 instance of `sympy.core.add.Add`
236 A Sympy expression representing the chromatic polynomial for `G`.
237
238 Examples
239 --------
240 >>> C = nx.cycle_graph(5)
241 >>> nx.chromatic_polynomial(C)
242 x**5 - 5*x**4 + 10*x**3 - 10*x**2 + 4*x
243
244 >>> G = nx.complete_graph(4)
245 >>> nx.chromatic_polynomial(G)
246 x**4 - 6*x**3 + 11*x**2 - 6*x
247
248 Notes
249 -----
250 Interpretation of the coefficients is discussed in [5]_. Several special
251 cases are listed in [2]_.
252
253 The chromatic polynomial is a specialization of the Tutte polynomial; in
254 particular, ``X_G(x) = T_G(x, 0)`` [6]_.
255
256 The chromatic polynomial may take negative arguments, though evaluations
257 may not have chromatic interpretations. For instance, ``X_G(-1)`` enumerates
258 the acyclic orientations of `G` [7]_.
259
260 References
261 ----------
262 .. [1] D. B. West,
263 "Introduction to Graph Theory," p. 222
264 .. [2] E. W. Weisstein
265 "Chromatic Polynomial"
266 MathWorld--A Wolfram Web Resource
267 https://mathworld.wolfram.com/ChromaticPolynomial.html
268 .. [3] D. B. West,
269 "Introduction to Graph Theory," p. 221
270 .. [4] J. Zhang, J. Goodall,
271 "An Introduction to Chromatic Polynomials"
272 https://math.mit.edu/~apost/courses/18.204_2018/Julie_Zhang_paper.pdf
273 .. [5] R. C. Read,
274 "An Introduction to Chromatic Polynomials"
275 Journal of Combinatorial Theory, 1968
276 https://math.berkeley.edu/~mrklug/ReadChromatic.pdf
277 .. [6] W. T. Tutte,
278 "Graph-polynomials"
279 Advances in Applied Mathematics, 2004
280 https://www.sciencedirect.com/science/article/pii/S0196885803000411
281 .. [7] R. P. Stanley,
282 "Acyclic orientations of graphs"
283 Discrete Mathematics, 2006
284 https://math.mit.edu/~rstan/pubs/pubfiles/18.pdf
285 """
286 import sympy
287
288 x = sympy.Symbol("x")
289 stack = deque()
290 stack.append(nx.MultiGraph(G, contraction_idx=0))
291
292 polynomial = 0
293 while stack:
294 G = stack.pop()
295 edges = list(G.edges)
296 if not edges:
297 polynomial += (-1) ** G.graph["contraction_idx"] * x ** len(G)
298 else:
299 e = edges[0]
300 C = nx.contracted_edge(G, e, self_loops=True)
301 C.graph["contraction_idx"] = G.graph["contraction_idx"] + 1
302 C.remove_edge(e[0], e[0])
303 G.remove_edge(*e)
304 stack.append(G)
305 stack.append(C)
306 return polynomial