1123 | | corresponding vertex), and M is the adjacency matrix. |
1124 | | |
1125 | | If weighted == True, the weighted adjacency matrix is used for M, |
1126 | | and the diagonal entries are the row-sums of M. |
| 1125 | corresponding vertex), and `M` is the adjacency matrix. |
| 1126 | |
| 1127 | ( In the special case of DiGraphs, `D` is defined as the diagonal |
| 1128 | in-degree matrix or diagonal out-degree matrix according to the |
| 1129 | value of ``indegree``) |
| 1130 | |
| 1131 | INPUT: |
| 1132 | |
| 1133 | - ``weighted`` -- Binary variable : |
| 1134 | - If ``weighted == True``, the weighted adjacency matrix is used for `M`, |
| 1135 | and the diagonal matrix `D` takes into account the weight of edges |
| 1136 | (replace in the definition "degree" by "sum of the incident edges" ). |
| 1137 | - Else, each edge is assumed to have weight 1. |
| 1138 | |
| 1139 | Default is to take weights into consideration if and only if the graph is |
| 1140 | weighted. |
| 1141 | |
| 1142 | - ``indegree`` -- Binary variable : |
| 1143 | - If ``indegree=True``, each diagonal entry of `D` is equal to the |
| 1144 | in-degree of the corresponding vertex. |
| 1145 | - Else, each diagonal entry of `D` is equal to the |
| 1146 | out-degree of the corresponding vertex. |
| 1147 | |
| 1148 | By default, ``indegree`` is set to ``True`` |
| 1149 | |
| 1150 | ( This variable only matters when the graph is a digraph ) |
1193 | | for (i,j), entry in M.dict().iteritems(): |
1194 | | row_sums[i] = row_sums.get(i, 0) + entry |
1195 | | else: |
1196 | | ones = matrix(M.base_ring(), M.nrows(), 1, [1]*M.nrows()) |
1197 | | S = M*ones |
1198 | | row_sums = dict((i, S[i,0]) for i in range(M.nrows())) |
1199 | | |
1200 | | for i in range(M.nrows()): |
1201 | | A[i,i] += row_sums.get(i, 0) |
| 1225 | if indegree: |
| 1226 | for (i,j), entry in M.dict().iteritems(): |
| 1227 | row_sums[j] = row_sums.get(j, 0) + entry |
| 1228 | else: |
| 1229 | for (i,j), entry in M.dict().iteritems(): |
| 1230 | row_sums[i] = row_sums.get(i, 0) + entry |
| 1231 | |
| 1232 | |
| 1233 | for i in range(M.nrows()): |
| 1234 | A[i,i] += row_sums.get(i, 0) |
| 1235 | |
| 1236 | else: |
| 1237 | if indegree: |
| 1238 | ones = matrix(M.base_ring(), 1, M.nrows(), [1]*M.nrows()) |
| 1239 | S = ones*M |
| 1240 | for i in range(M.nrows()): |
| 1241 | A[i,i] += S[0,i] |
| 1242 | else: |
| 1243 | ones = matrix(M.base_ring(), M.nrows(), 1, [1]*M.nrows()) |
| 1244 | S = M*ones |
| 1245 | for i in range(M.nrows()): |
| 1246 | A[i,i] += S[i,0] |
| 2153 | def spanning_trees_count(self, root_vertex=None): |
| 2154 | """ |
| 2155 | Returns the number of spanning trees in a graph. In the case of a |
| 2156 | digraph, couts the number of spanning out-trees rooted in |
| 2157 | ``root_vertex``. |
| 2158 | Default is to set first vertex as root. |
| 2159 | |
| 2160 | This computation uses Kirchhoff's Matrix Tree Theorem [1] to calculate |
| 2161 | the number of spanning trees. For complete graphs on `n` vertices the |
| 2162 | result can also be reached using Cayley's formula: the number of |
| 2163 | spanning trees are `n^(n-2)`. |
| 2164 | |
| 2165 | For digraphs, the augmented Kirchhoff Matrix as defined in [2] is |
| 2166 | used for calculations. Here the result is the number of out-trees |
| 2167 | rooted at a specific vertex. |
| 2168 | |
| 2169 | INPUT: |
| 2170 | |
| 2171 | - ``root_vertex`` -- integer (default: the first vertex) This is the vertex |
| 2172 | that will be used as root for all spanning out-trees if the graph |
| 2173 | is a directed graph. |
| 2174 | This argument is ignored if the graph is not a digraph. |
| 2175 | |
| 2176 | REFERENCES: |
| 2177 | |
| 2178 | - [1] http://mathworld.wolfram.com/MatrixTreeTheorem.html |
| 2179 | |
| 2180 | - [2] Lih-Hsing Hsu, Cheng-Kuan Lin, "Graph Theory and Interconnection |
| 2181 | Networks" |
| 2182 | |
| 2183 | AUTHORS: |
| 2184 | |
| 2185 | - Anders Jonsson (2009-10-10) |
| 2186 | |
| 2187 | EXAMPLES:: |
| 2188 | |
| 2189 | sage: G = graphs.PetersenGraph() |
| 2190 | sage: G.spanning_trees_count() |
| 2191 | 2000 |
| 2192 | |
| 2193 | :: |
| 2194 | |
| 2195 | sage: n = 11 |
| 2196 | sage: G = graphs.CompleteGraph(n) |
| 2197 | sage: ST = G.spanning_trees_count() |
| 2198 | sage: ST == n^(n-2) |
| 2199 | True |
| 2200 | |
| 2201 | :: |
| 2202 | |
| 2203 | sage: M=matrix(3,3,[0,1,0,0,0,1,1,1,0]) |
| 2204 | sage: D=DiGraph(M) |
| 2205 | sage: D.spanning_trees_count() |
| 2206 | 1 |
| 2207 | sage: D.spanning_trees_count(0) |
| 2208 | 1 |
| 2209 | sage: D.spanning_trees_count(2) |
| 2210 | 2 |
| 2211 | |
| 2212 | """ |
| 2213 | if self.is_directed() == False: |
| 2214 | M=self.kirchhoff_matrix() |
| 2215 | M.subdivide(1,1) |
| 2216 | M2 = M.subdivision(1,1) |
| 2217 | return abs(M2.determinant()) |
| 2218 | else: |
| 2219 | if root_vertex == None: |
| 2220 | root_vertex=self.vertex_iterator().next() |
| 2221 | if root_vertex not in self.vertices(): |
| 2222 | raise ValueError, ("Vertex (%s) not in the graph."%root_vertex) |
| 2223 | |
| 2224 | M=self.kirchhoff_matrix() |
| 2225 | |
| 2226 | index=self.vertices().index(root_vertex) |
| 2227 | M[index,index]+=1 |
| 2228 | return abs(M.determinant()) |
| 2229 | |