Ticket #14589: trac_14589.patch
File trac_14589.patch, 15.0 KB (added by , 9 years ago) 


doc/en/reference/graphs/index.rst
# HG changeset patch # User Nathann Cohen <nathann.cohen@gmail.com> # Date 1368613532 7200 # Wed May 15 12:25:32 2013 +0200 # Node ID 88fdb1f3fbcd8a3587f912dd4bf97a3f7f58751a # Parent 0eee5ed9955fd55b5fb02b524b1c9a59067234eb binary matrices, dense graphs, and faster is_strongly_regular diff git a/doc/en/reference/graphs/index.rst b/doc/en/reference/graphs/index.rst
a b 38 38 sage/graphs/base/sparse_graph 39 39 sage/graphs/base/dense_graph 40 40 sage/graphs/base/static_sparse_graph 41 sage/graphs/base/static_dense_graph 41 42 sage/graphs/base/graph_backends 42 43 43 44 Hypergraphs 
module_list.py
diff git a/module_list.py b/module_list.py
a b 383 383 Extension('sage.graphs.base.static_sparse_graph', 384 384 sources = ['sage/graphs/base/static_sparse_graph.pyx']), 385 385 386 Extension('sage.graphs.base.static_dense_graph', 387 sources = ['sage/graphs/base/static_dense_graph.pyx']), 388 386 389 Extension('sage.graphs.modular_decomposition.modular_decomposition', 387 390 sources = ['sage/graphs/modular_decomposition/modular_decomposition.pyx', 388 391 'sage/graphs/modular_decomposition/src/dm.c'], 
sage/graphs/base/all.py
diff git a/sage/graphs/base/all.py b/sage/graphs/base/all.py
a b 1 1 from sparse_graph import SparseGraph 2 2 from dense_graph import DenseGraph 3 3 import sage.graphs.base.static_sparse_graph 4 import sage.graphs.base.static_dense_graph 
new file sage/graphs/base/static_dense_graph.pyx
diff git a/sage/graphs/base/static_dense_graph.pyx b/sage/graphs/base/static_dense_graph.pyx new file mode 100644
 + 1 r""" 2 Static dense graphs 3 4 This module gathers everything which is related to static dense graphs, i.e. : 5 6  The vertices are integer from `0` to `n1` 7  No labels on vertices/edges 8  No multiple edges 9  No addition/removal of vertices 10 11 This being said, it is technically possible to add/remove edges. The data 12 structure does not mind at all. 13 14 It is all based on the binary matrix data structure described in 15 ``misc/binary_matrix.pxi``, which is almost a copy of the bitset data 16 structure. The only difference is that it differentiates the rows (the vertices) 17 instead of storing the whole data in a long bitset, and we can use that. 18 19 Index 20  21 22 **Cython functions** 23 24 .. csvtable:: 25 :class: contentstable 26 :widths: 30, 70 27 :delim:  28 29 ``dense_graph_init``  Fills a binary matrix with the information of a (di)graph. 30 31 **Python functions** 32 33 .. csvtable:: 34 :class: contentstable 35 :widths: 30, 70 36 :delim:  37 38 :meth:`is_strongly_regular`  Tests if a graph is strongly regular 39 40 Functions 41  42 """ 43 include "sage/misc/binary_matrix.pxi" 44 45 cdef dict dense_graph_init(binary_matrix_t m, g, translation = False): 46 r""" 47 Initializes the binary matrix from a Sage (di)graph. 48 49 INPUT: 50 51  ``binary_matrix_t m``  the binary matrix to be filled 52 53  ``g``  a graph or digraph 54 55  ``translation`` (boolean)  whether to return a dictionary associating to 56 each vertex its corresponding integer in the binary matrix. 57 """ 58 cdef dict d_translation 59 from sage.graphs.graph import Graph 60 cdef int is_undirected = isinstance(g, Graph) 61 cdef int n = g.order() 62 63 binary_matrix_init(m,n,n) 64 binary_matrix_fill(m,0) 65 66 # If the vertices are 0...n1, let's avoid an unnecessary dictionary 67 if g.vertices() == range(n): 68 if translation: 69 d_translation = {i:i for i in range(n)} 70 71 for i,j in g.edge_iterator(labels = False): 72 binary_matrix_set1(m,i,j) 73 if is_undirected: 74 binary_matrix_set1(m,j,i) 75 else: 76 d_translation = {v:i for i,v in enumerate(g.vertices())} 77 78 for u,v in g.edge_iterator(labels = False): 79 binary_matrix_set1(m,d_translation[u],d_translation[v]) 80 if is_undirected: 81 binary_matrix_set1(m,d_translation[v],d_translation[u]) 82 83 if translation: 84 return d_translation 85 86 def is_strongly_regular(g, parameters = False): 87 r""" 88 Tests whether ``self`` is strongly regular. 89 90 A graph `G` is said to be strongly regular with parameters `(n, k, \lambda, 91 \mu)` if and only if: 92 93 * `G` has `n` vertices. 94 95 * `G` is `k`regular. 96 97 * Any two adjacent vertices of `G` have `\lambda` common neighbors. 98 99 * Any two nonadjacent vertices of `G` have `\mu` common neighbors. 100 101 By convention, the complete graphs, the graphs with no edges 102 and the empty graph are not strongly regular. 103 104 See :wikipedia:`Strongly regular graph` 105 106 INPUT: 107 108  ``parameters`` (boolean)  whether to return the quadruple `(n, 109 k,\lambda,\mu)`. If ``parameters = False`` (default), this method only 110 returns ``True`` and ``False`` answers. If ``parameters=True``, the 111 ``True`` answers are replaced by quadruples `(n, k,\lambda,\mu)`. See 112 definition above. 113 114 EXAMPLES: 115 116 Petersen's graph is strongly regular:: 117 118 sage: g = graphs.PetersenGraph() 119 sage: g.is_strongly_regular() 120 True 121 sage: g.is_strongly_regular(parameters = True) 122 (10, 3, 0, 1) 123 124 And Clebsch's graph is too:: 125 126 sage: g = graphs.ClebschGraph() 127 sage: g.is_strongly_regular() 128 True 129 sage: g.is_strongly_regular(parameters = True) 130 (16, 5, 0, 2) 131 132 But Chvatal's graph is not:: 133 134 sage: g = graphs.ChvatalGraph() 135 sage: g.is_strongly_regular() 136 False 137 138 Complete graphs are not strongly regular. (:trac:`14297`) :: 139 140 sage: g = graphs.CompleteGraph(5) 141 sage: g.is_strongly_regular() 142 False 143 144 Completements of complete graphs are not strongly regular:: 145 146 sage: g = graphs.CompleteGraph(5).complement() 147 sage: g.is_strongly_regular() 148 False 149 150 The empty graph is not strongly regular:: 151 152 sage: g = graphs.EmptyGraph() 153 sage: g.is_strongly_regular() 154 False 155 """ 156 cdef binary_matrix_t m 157 cdef int n = g.order() 158 cdef int inter 159 cdef int i,j,l, k 160 161 if g.size() == 0: # no vertices or no edges 162 return False 163 164 if g.is_clique(): 165 return False 166 167 cdef list degree = g.degree() 168 k = degree[0] 169 if not all(d == k for d in degree): 170 return False 171 172 # m i now our copy of the graph 173 dense_graph_init(m, g) 174 175 cdef int llambda = 1 176 cdef int mu = 1 177 178 for i in range(n): 179 for j in range(i+1,n): 180 181 # The intersection of the common neighbors of i and j is a AND of 182 # their respective rows. A popcount then returns its cardinality. 183 inter = 0 184 for l in range(m.width): 185 inter += __builtin_popcountl(m.rows[i][l] & m.rows[j][l]) 186 187 # Check that this cardinality is correct according to the values of lambda and mu 188 if binary_matrix_get(m,i,j): 189 if llambda == 1: 190 llambda = inter 191 elif llambda != inter: 192 binary_matrix_free(m) 193 return False 194 else: 195 if mu == 1: 196 mu = inter 197 elif mu != inter: 198 binary_matrix_free(m) 199 return False 200 201 binary_matrix_free(m) 202 203 if parameters: 204 return (n,k,llambda,mu) 205 else: 206 return True 
sage/graphs/graph.py
diff git a/sage/graphs/graph.py b/sage/graphs/graph.py
a b 2452 2452 2453 2453 return self_complement.is_odd_hole_free(certificate = certificate) 2454 2454 2455 def is_strongly_regular(self, parameters=False):2456 r"""2457 Tests whether ``self`` is strongly regular.2458 2459 A graph `G` is said to be strongly regular with parameters (n, k,2460 \lambda, \mu)` if and only if:2461 2462 * `G` has `n` vertices.2463 2464 * `G` is `k`regular.2465 2466 * Any two adjacent vertices of `G` have `\lambda` common neighbors.2467 2468 * Any two nonadjacent vertices of `G` have `\mu` common neighbors.2469 2470 By convention, the complete graphs, the graphs with no edges2471 and the empty graph are not strongly regular.2472 2473 See :wikipedia:`Strongly regular graph`2474 2475 INPUT:2476 2477  ``parameters`` (boolean)  whether to return the quadruple `(n,2478 k,\lambda,\mu)`. If ``parameters = False`` (default), this method only2479 returns ``True`` and ``False`` answers. If ``parameters=True``, the2480 ``True`` answers are replaced by quadruples `(n, k,\lambda,\mu)`. See2481 definition above.2482 2483 EXAMPLES:2484 2485 Petersen's graph is strongly regular::2486 2487 sage: g = graphs.PetersenGraph()2488 sage: g.is_strongly_regular()2489 True2490 sage: g.is_strongly_regular(parameters = True)2491 (10, 3, 0, 1)2492 2493 And Clebsch's graph is too::2494 2495 sage: g = graphs.ClebschGraph()2496 sage: g.is_strongly_regular()2497 True2498 sage: g.is_strongly_regular(parameters = True)2499 (16, 5, 0, 2)2500 2501 But Chvatal's graph is not::2502 2503 sage: g = graphs.ChvatalGraph()2504 sage: g.is_strongly_regular()2505 False2506 2507 Complete graphs are not strongly regular. (:trac:`14297`) ::2508 2509 sage: g = graphs.CompleteGraph(5)2510 sage: g.is_strongly_regular()2511 False2512 2513 Completements of graphs are not strongly regular::2514 2515 sage: g = graphs.CompleteGraph(5).complement()2516 sage: g.is_strongly_regular()2517 False2518 2519 The empty graph is not strongly regular::2520 2521 sage: g = graphs.EmptyGraph()2522 sage: g.is_strongly_regular()2523 False2524 """2525 2526 if self.size() == 0: # no vertices or no edges2527 return False2528 2529 degree = self.degree()2530 k = degree[0]2531 if not all(d == k for d in degree):2532 return False2533 2534 if self.is_clique():2535 return False2536 else:2537 l = m = None2538 for u in self:2539 nu = set(self.neighbors(u))2540 for v in self:2541 if u == v:2542 continue2543 nv = set(self.neighbors(v))2544 inter = len(nu&nv)2545 2546 if v in nu:2547 if l is None:2548 l = inter2549 else:2550 if l != inter:2551 return False2552 else:2553 if m is None:2554 m = inter2555 else:2556 if m != inter:2557 return False2558 2559 if parameters:2560 return (self.order(),k,l,m)2561 else:2562 return True2563 2564 2455 def odd_girth(self): 2565 2456 r""" 2566 2457 Returns the odd girth of self. … … 6114 6005 import sage.graphs.graph_decompositions.graph_products 6115 6006 Graph.is_cartesian_product = types.MethodType(sage.graphs.graph_decompositions.graph_products.is_cartesian_product, None, Graph) 6116 6007 6008 import sage.graphs.base.static_dense_graph 6009 Graph.is_strongly_regular = types.MethodType(sage.graphs.base.static_dense_graph.is_strongly_regular, None, Graph) 6010 6117 6011 # From Python modules 6118 6012 import sage.graphs.line_graph 6119 6013 Graph.is_line_graph = sage.graphs.line_graph.is_line_graph 
new file sage/misc/binary_matrix.pxi
diff git a/sage/misc/binary_matrix.pxi b/sage/misc/binary_matrix.pxi new file mode 100644
 + 1 r""" 2 A binary matrix datatype in Cython 3 4 It's almost a copy of the bitset datatype, but allows a differentiation of the 5 rows of the matrix. That's the only advantage compared to storing all the rows 6 of a matrix in a loooooonng bitset. 7 8 a ``binary_matrix_t`` structure contains : 9 10  ``long n_cols``  number of columns 11 12  ``long n_rows``  number of rows 13 14  ``long width``  number of ``unsigned long`` per row 15 16  ``unsigned long ** rows``  ``rows[i]`` points toward the ``width`` blocks of 17 type ``unsigned long`` containing the bits of row `i`. 18 19 .. NOTE:: 20 21 The rows are stored contiguously in memory, i.e. ``row[i] = row[i1] + 22 width``. 23 """ 24 include "binary_matrix_pxd.pxi" 25 26 cdef inline binary_matrix_init(binary_matrix_t m, long n_rows, long n_cols): 27 r""" 28 Allocates the binary matrix. 29 """ 30 cdef int i 31 32 m.n_cols = n_cols 33 m.n_rows = n_rows 34 m.width = (n_cols  1)/(8*sizeof(unsigned long)) + 1 35 m.rows = <unsigned long**>sage_malloc(n_rows * sizeof(unsigned long *)) 36 if m.rows == NULL: 37 raise MemoryError 38 39 m.rows[0] = <unsigned long*>sage_malloc(n_rows * m.width * sizeof(unsigned long)) 40 if m.rows[0] == NULL: 41 sage_free(m.rows) 42 raise MemoryError 43 44 for i in range(1,n_rows): 45 m.rows[i] = m.rows[i1] + m.width 46 m.rows[i][m.width1] = 0 47 48 cdef inline binary_matrix_free(binary_matrix_t m): 49 r""" 50 Frees the memory allocated by the matrix 51 """ 52 sage_free(m.rows[0]) 53 sage_free(m.rows) 54 55 cdef inline binary_matrix_fill(binary_matrix_t m, bint bit): 56 r""" 57 Fill the whole matrix with a bit 58 """ 59 memset(m.rows[0],(<char> bit),m.width * m.n_rows * sizeof(unsigned long)) 60 61 cdef inline binary_matrix_set1(binary_matrix_t m, long row, long col): 62 r""" 63 Sets an entry to 1 64 """ 65 m.rows[row][col >> index_shift] = (<unsigned long>1) << (col & offset_mask) 66 67 cdef inline binary_matrix_set0(binary_matrix_t m, long row, long col): 68 r""" 69 Sets an entry to 1 70 """ 71 m.rows[row][col >> index_shift] &= ~((<unsigned long>1) << (col & offset_mask)) 72 73 cdef inline binary_matrix_set(binary_matrix_t m, long row, long col, bint value): 74 r""" 75 Sets an entry 76 """ 77 binary_matrix_set0(m,row,col) 78 m.rows[row][col >> index_shift] = (<unsigned long>1) << (value & offset_mask) 79 80 cdef inline bint binary_matrix_get(binary_matrix_t m, long row, long col): 81 r""" 82 Returns the value of a given entry 83 """ 84 return (m.rows[row][col >> index_shift] >> (col & offset_mask)) & 1 85 86 cdef inline binary_matrix_print(binary_matrix_t m): 87 r""" 88 Prints the binary matrix 89 """ 90 cdef int i,j 91 import sys 92 for i in range(m.n_rows): 93 for j in range(m.n_cols): 94 sys.stdout.write("1" if binary_matrix_get(m, i, j) else ".",) 95 print "" 
new file sage/misc/binary_matrix_pxd.pxi
diff git a/sage/misc/binary_matrix_pxd.pxi b/sage/misc/binary_matrix_pxd.pxi new file mode 100644
 + 1 include "bitset_pxd.pxi" 2 include "bitset.pxi" 3 4 cdef extern from *: 5 int __builtin_popcountl(unsigned long) 6 7 cdef struct binary_matrix_s: 8 long n_cols 9 long n_rows 10 11 # Number of "unsigned long" per row 12 long width 13 14 unsigned long ** rows 15 16 ctypedef binary_matrix_s[1] binary_matrix_t 17