Ticket #12917: trac_12917.patch

File trac_12917.patch, 14.9 KB (added by jdemeyer, 9 years ago)

Rebased to sage-5.1.rc0

  • doc/en/reference/graphs.rst

    # HG changeset patch
    # User Nathann Cohen <nathann.cohen@gmail.com>
    # Date 1336381478 -7200
    # Node ID c4b578bcf818536c1edb5075967f0c5497ffcd6b
    # Parent  b7e6f049ea42bae8907a0a52b098d71373afce4e
    Method Graph.is_cartesian_product
    
    diff --git a/doc/en/reference/graphs.rst b/doc/en/reference/graphs.rst
    a b  
    5757   sage/graphs/graph_plot
    5858   sage/graphs/graph_decompositions/vertex_separation
    5959   sage/graphs/graph_decompositions/rankwidth
     60   sage/graphs/graph_decompositions/graph_products
    6061   sage/graphs/modular_decomposition/modular_decomposition
    6162   sage/graphs/convexity_properties
    6263   sage/graphs/distances_all_pairs
  • module_list.py

    diff --git a/module_list.py b/module_list.py
    a b  
    338338    Extension('sage.graphs.graph_decompositions.vertex_separation',
    339339              sources = ['sage/graphs/graph_decompositions/vertex_separation.pyx']),
    340340
     341    Extension('sage.graphs.graph_decompositions.graph_products',
     342              sources = ['sage/graphs/graph_decompositions/graph_products.pyx']),
     343
    341344    Extension('sage.graphs.convexity_properties',
    342345              sources = ['sage/graphs/convexity_properties.pyx']),
    343346
  • sage/graphs/generic_graph.py

    diff --git a/sage/graphs/generic_graph.py b/sage/graphs/generic_graph.py
    a b  
    8888    :meth:`~GenericGraph.subgraph` | Returns the subgraph containing the given vertices and edges.
    8989    :meth:`~GenericGraph.is_subgraph` | Tests whether self is a subgraph of other.
    9090
    91 
    92 
    9391**Graph products:**
    9492
    9593.. csv-table::
     
    1311513113        and `((u,v), (w,x))` is an edge iff either - `(u, w)` is an edge of self
    1311613114        and `v = x`, or - `(v, x)` is an edge of other and `u = w`.
    1311713115
     13116        .. SEEALSO::
     13117
     13118            - :meth:`~sage.graphs.graph_decompositions.graph_products.is_cartesian_product`
     13119              -- factorization of graphs according to the cartesian product
     13120
     13121            - :mod:`~sage.graphs.graph_decompositions.graph_products`
     13122              -- a module on graph products.
     13123
    1311813124        TESTS:
    1311913125
    1312013126        Cartesian product of graphs::
  • sage/graphs/graph.py

    diff --git a/sage/graphs/graph.py b/sage/graphs/graph.py
    a b  
    4848    :meth:`~Graph.is_line_graph` | Tests wether the graph is a line graph.
    4949    :meth:`~Graph.is_odd_hole_free` | Tests whether ``self`` contains an induced odd hole.
    5050    :meth:`~Graph.is_even_hole_free` | Tests whether ``self`` contains an induced even hole.
     51    :meth:`~Graph.is_cartesian_product` | Tests whether ``self`` is a cartesian product of graphs.
    5152
    5253
    5354
     
    491492from sage.graphs.generic_graph import GenericGraph
    492493from sage.graphs.digraph import DiGraph
    493494
     495
    494496class Graph(GenericGraph):
    495497    r"""
    496498    Undirected graph.
     
    47074709
    47084710        return D[0] == "Prime" and len(D[1]) == self.order()
    47094711
     4712    def is_cartesian_product(self, certificate = False):
     4713        r"""
     4714        Tests whether ``self`` is a cartesian product of graphs.
     4715
     4716        INPUT:
     4717
     4718        - ``certificate`` (boolean) -- if ``certificate = False`` (default) the
     4719          method only returns ``True`` or ``False`` answers. If ``certificate =
     4720          True``, the ``True`` answers are replaced by the list of the factors of
     4721          the graph.
     4722
     4723        .. SEEALSO::
     4724
     4725            - :meth:`~sage.graphs.generic_graph.GenericGraph.cartesian_product`
     4726
     4727            - :mod:`~sage.graphs.graph_decompositions.graph_products` -- a
     4728              module on graph products.
     4729
     4730        EXAMPLE:
     4731
     4732        The Petersen graph is prime::
     4733
     4734            sage: g = graphs.PetersenGraph()
     4735            sage: g.is_cartesian_product()
     4736            False
     4737
     4738        A 2d grid is the product of paths::
     4739
     4740            sage: g = graphs.Grid2dGraph(5,5)
     4741            sage: p1, p2 = g.is_cartesian_product(certificate = True)
     4742            sage: p1.is_isomorphic(graphs.PathGraph(5))
     4743            True
     4744            sage: p2.is_isomorphic(graphs.PathGraph(5))
     4745            True
     4746
     4747        And of course, we find the factors back when we build a graph from a
     4748        product::
     4749
     4750            sage: g = graphs.PetersenGraph().cartesian_product(graphs.CycleGraph(3))
     4751            sage: g1, g2 = g.is_cartesian_product(certificate = True)
     4752            sage: any( x.is_isomorphic(graphs.PetersenGraph()) for x in [g1,g2])
     4753            True
     4754            sage: any( x.is_isomorphic(graphs.CycleGraph(3)) for x in [g1,g2])
     4755            True
     4756        """
     4757        from sage.graphs.graph_decompositions.graph_products import is_cartesian_product
     4758        return is_cartesian_product(self, certificate = certificate)
     4759
     4760
    47104761    def _gomory_hu_tree(self, vertices=None, method="FF"):
    47114762        r"""
    47124763        Returns a Gomory-Hu tree associated to self.
  • new file sage/graphs/graph_decompositions/graph_products.pyx

    diff --git a/sage/graphs/graph_decompositions/graph_products.pyx b/sage/graphs/graph_decompositions/graph_products.pyx
    new file mode 100644
    - +  
     1r"""
     2Products of graphs
     3
     4This module gathers everything related to graph products. At the moment it
     5contains different implementations of recognition algorithms for graphs that can
     6be written as a cartesian product of smaller ones.
     7
     8References:
     9
     10  .. [HIK11] Handbook of Product Graphs,
     11    R. Hammack, W. Imrich, S. Klavzar,
     12    CRC press, 2011
     13
     14Author:
     15
     16- Nathann Cohen (May 2012 -- coded while watching the election of Francois
     17  Hollande on TV)
     18
     19Cartesian product of graphs -- the recognition problem
     20------------------------------------------------------
     21
     22First, a definition:
     23
     24  **Definition** The cartesian product of two graphs `G` and `H`, denoted
     25  `G\square H`, is a graph defined on the pairs `(g, h)\in V(G)\times V(H)`.
     26
     27  Two elements `(g, h),(g', h')\in V(G\square H)` are adjacent in `G\square H`
     28  if and only if :
     29
     30  - `g=g'` and `hh'\in H`; or
     31  - `h=h'` and `gg'\in H`
     32
     33Two remarks follow :
     34
     35#. The cartesian product is commutative
     36
     37#. Any edge `uv` of a graph `G_1 \square \cdots \square G_k` can be given a color
     38   `i` corresponding to the unique index `i` such that `u_i` and `v_i` differ.
     39
     40The problem that is of interest to us in the present module is the following:
     41
     42  **Recognition problem** Given a graph `G`, can we guess whether there exist
     43  graphs `G_1, ..., G_k` such that `G=G_1\square \cdots \square G_k` ?
     44
     45This problem can actually be solved, and the resulting factorization is
     46unique. What is explained below can be found in the book *Handbook of Product
     47Graphs* [HIK11]_.
     48
     49Everything is actually based on a simple observation. Given a graph `G`, finding
     50out whether `G` can be written as the product of several graphs can be attempted
     51by trying to color its edges according to some rules. Indeed, if we are to color
     52the edges of `G` in such a way that each color class represents a factor of `G`,
     53we must ensure several things.
     54
     55  **Remark 1** In any cycle of `G` no color can appear exactly once.
     56
     57  Indeed, if only one edge `uv` of a cycle were labelled with color `i`, it
     58  would mean that:
     59
     60  #. The only difference between `u` and `v` lies in their `i` th coordinate
     61
     62  #. It is possible to go from `u` to `v` by changing only coordinates
     63     different from the `i` th
     64
     65  A contradiction indeed.
     66
     67  .. MATH::
     68
     69      \begin{picture}(200,200)(-100,-100)
     70      \put(-50,0){\circle*{5}}
     71      \put(50,0){\circle*{5}}
     72      \put(-25,-35){\circle*{5}}
     73      \put(25,-35){\circle*{5}}
     74      \put(-25,35){\circle*{5}}
     75      \put(25,35){\circle*{5}}
     76      \qbezier(-50,0)(-37.5, 17.5)(-25,35)
     77      \qbezier(-50,0)(-37.5, -17.5)(-25,-35)
     78      \qbezier(50,0)(37.5, 17.5)(25,35)
     79      \qbezier(50,0)(37.5, -17.5)(25,-35)
     80      \qbezier(-25,35)(0,35)(25,35)
     81      \linethickness{1.5pt}
     82      \qbezier[10](-25,-35)(0,-35)(25,-35)
     83      \put(0,0){\makebox(0,0){No way !!!}}
     84      \end{picture}
     85
     86  That means that, for instance, the edges of a triangle necessarily have the
     87  same color.
     88
     89  **Remark 2** If two consecutive edges `u_1u_2` and `u_2u_3` have different
     90  colors, there necessarily exists a unique vertex `u_4` different from `u_2`
     91  and incident to both `u_1` and `u_3`.
     92
     93  In this situation, opposed edges necessarily have the same colors because of
     94  the previous remark.
     95
     96  .. MATH::
     97
     98    \begin{picture}(200,200)(-100,-100)
     99    \put(-50,0){\circle*{5}}
     100    \put(50,0){\circle*{5}}
     101    \put(0,50){\circle*{5}}
     102    \put(0,-50){\circle*{5}}
     103    \qbezier(50,0)(25,25)(0,50)
     104    \qbezier(-50,0)(-25,-25)(0,-50)
     105    \linethickness{1.5pt}
     106    \qbezier[10](0,50)(-25,25)(-50,0)
     107    \qbezier[10](0,-50)(25,-25)(50,0)
     108    \put(0,55){\makebox(0,0)[b]{$u_3=(g',h')$}}
     109    \put(0,-55){\makebox(0,0)[t]{$u_1=(g,h)$}}
     110    \put(55,0){\makebox(0,0)[l]{$u_4=(g,h')$}}
     111    \put(-55,0){\makebox(0,0)[r]{$u_2=(g',h)$}}
     112    \end{picture}
     113
     114  As a corollary, we also know that:
     115
     116  #. If two vertices `u,v` have a *unique* common neighbor `x`, then `ux` and
     117     `uv` have the same color.
     118
     119  #. If two vertices `u, v` have more that two common neighbors `x_1, ...,
     120     x_k` then all edges between the `x_i` and the vertices of `u,v` have the
     121     same color. This is also a consequence of the first remark.
     122
     123The algorithm
     124^^^^^^^^^^^^^
     125
     126The previous remarks tell us that some edges are in some way equivalent to some
     127others, i.e. that their colors are equal. In order to compute the coloring we
     128are looking for, we therefore build a graph on the *edges* of a graph `G`,
     129linking two edges whenever they are found to be equivalent according to the
     130previous remarks.
     131
     132All that is left to do is to compute the connected components of this new graph,
     133as each of them representing the edges of a factor. Of course, only one
     134connected component indicates that the graph has no factorization.
     135
     136Then again, please refer to [HIK11]_ for any technical question.
     137
     138Methods
     139-------
     140"""
     141
     142#******************************************************************************
     143#          Copyright (C) 2012 Nathann Cohen <nathann.cohen@gmail.com>         *
     144#                                                                             *
     145# Distributed  under  the  terms  of  the  GNU  General  Public  License (GPL)*
     146#                         http://www.gnu.org/licenses/                        *
     147#******************************************************************************
     148
     149def is_cartesian_product(g, certificate = False):
     150    r"""
     151    Tests whether the graph can be factorized.
     152
     153    INPUT:
     154
     155    - ``certificate`` (boolean) -- if ``certificate = False`` (default) the
     156      method only returns ``True`` or ``False`` answers. If ``certificate =
     157      True``, the ``True`` answers are replaced by the list of the factors of
     158      the graph.
     159
     160    .. SEEALSO::
     161
     162        - :meth:`~sage.graphs.generic_graph.GenericGraph.cartesian_product`
     163
     164    EXAMPLE:
     165
     166    The Petersen graph is prime::
     167
     168        sage: from sage.graphs.graph_decompositions.graph_products import is_cartesian_product
     169        sage: g = graphs.PetersenGraph()
     170        sage: is_cartesian_product(g)
     171        False
     172
     173    A 2d grid is the product of paths::
     174
     175        sage: g = graphs.Grid2dGraph(5,5)
     176        sage: p1, p2 = is_cartesian_product(g, certificate = True)
     177        sage: p1.is_isomorphic(graphs.PathGraph(5))
     178        True
     179        sage: p2.is_isomorphic(graphs.PathGraph(5))
     180        True
     181
     182    And of course, we find the factors back when we build a graph from a
     183    product::
     184
     185        sage: g = graphs.PetersenGraph().cartesian_product(graphs.CycleGraph(3))
     186        sage: g1, g2 = is_cartesian_product(g, certificate = True)
     187        sage: any( x.is_isomorphic(graphs.PetersenGraph()) for x in [g1,g2])
     188        True
     189        sage: any( x.is_isomorphic(graphs.CycleGraph(3)) for x in [g1,g2])
     190        True
     191    """
     192    from sage.rings.integer import Integer
     193    H = g
     194
     195    # Of course the number of vertices of g can not be prime !
     196    if Integer(g.order()).is_prime():
     197        return False
     198    if not g.is_connected():
     199        raise ValueError("The graph must be connected !")
     200
     201    from sage.graphs.graph import Graph
     202
     203    # As we need the vertices of g to be linearly ordered, we copy the graph and
     204    # relabel it
     205    g = g.copy()
     206    trev = g.relabel(return_map = True)
     207    t = dict([(x,y) for y,x in trev.iteritems()])
     208
     209    # Reorder the vertices of an edge
     210    r = lambda x,y : (x,y) if x<y else (y,x)
     211
     212    # The equivalence graph on the edges of g
     213    h = Graph()
     214    h.add_vertices([r(x,y) for x, y in g.edges(labels = False)])
     215
     216    # For all pairs of vertices u,v of G, according to their number of common
     217    # neighbors... See the module's documentation !
     218    for u in g:
     219        un = set(g.neighbors(u))
     220        for v in g.breadth_first_search(u):
     221
     222            # u and v are different
     223            if u == v:
     224                continue
     225
     226            # List of common neighbors
     227            intersect = un & set(g.neighbors(v))
     228
     229            # If u and v have no neighbors and uv is not an edge then their
     230            # distance is at least 3. As we enumerate the vertices in a
     231            # breadth-first search, it means that we already checked all the
     232            # vertices at distance less than two from u, and we are done with
     233            # this loop !
     234            if not intersect:
     235                if g.has_edge(u,v):
     236                    continue
     237                else:
     238                    break
     239
     240            # If uv is an edge
     241            if g.has_edge(u,v):
     242                h.add_path([r(u,x) for x in intersect] + [r(v,x) for x in intersect])
     243
     244            # Only one common neighbor
     245            elif len(intersect) == 1:
     246                x = intersect.pop()
     247                h.add_edge(r(u,x),r(v,x))
     248
     249            # Exactly 2 neighbors
     250            elif len(intersect) == 2:
     251                x,y = intersect
     252                h.add_edge(r(u,x),r(v,y))
     253                h.add_edge(r(v,x),r(u,y))
     254            # More
     255            else:
     256                h.add_path([r(u,x) for x in intersect] + [r(v,x) for x in intersect])
     257
     258    # Gathering the connected components, relabeling the vertices on-the-fly
     259    edges = map(lambda x:map(lambda y : (t[y[0]],t[y[1]]),x),h.connected_components())
     260
     261    # Only one connected component ?
     262    if len(edges) == 1:
     263        return False
     264
     265    # Building the list of factors
     266    factors = []
     267    for cc in edges:
     268        tmp = Graph()
     269        tmp.add_edges(cc)
     270        factors.append(tmp.subgraph(vertices = tmp.connected_components()[0]))
     271
     272    # Computing the product of these graphs
     273    answer = factors[0]
     274    for i in range(1,len(factors)):
     275        answer = answer.cartesian_product(factors[i])
     276
     277    # Checking that the resulting graph is indeed isomorphic to what we have.
     278    if not answer.is_isomorphic(g):
     279        raise ValueError("Something weird happened during the algorithm... "+
     280                         "Please report the bug and give us the graph instance"+
     281                         " that made it fail !!!")
     282
     283    if certificate:
     284        return factors
     285    else:
     286        return True