# HG changeset patch
# User Nathann Cohen
# 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/doc/en/reference/graphs.rst
+++ b/doc/en/reference/graphs.rst
@@ 57,6 +57,7 @@
sage/graphs/graph_plot
sage/graphs/graph_decompositions/vertex_separation
sage/graphs/graph_decompositions/rankwidth
+ sage/graphs/graph_decompositions/graph_products
sage/graphs/modular_decomposition/modular_decomposition
sage/graphs/convexity_properties
sage/graphs/distances_all_pairs
diff git a/module_list.py b/module_list.py
 a/module_list.py
+++ b/module_list.py
@@ 338,6 +338,9 @@
Extension('sage.graphs.graph_decompositions.vertex_separation',
sources = ['sage/graphs/graph_decompositions/vertex_separation.pyx']),
+ Extension('sage.graphs.graph_decompositions.graph_products',
+ sources = ['sage/graphs/graph_decompositions/graph_products.pyx']),
+
Extension('sage.graphs.convexity_properties',
sources = ['sage/graphs/convexity_properties.pyx']),
diff git a/sage/graphs/generic_graph.py b/sage/graphs/generic_graph.py
 a/sage/graphs/generic_graph.py
+++ b/sage/graphs/generic_graph.py
@@ 88,8 +88,6 @@
:meth:`~GenericGraph.subgraph`  Returns the subgraph containing the given vertices and edges.
:meth:`~GenericGraph.is_subgraph`  Tests whether self is a subgraph of other.


**Graph products:**
.. csvtable::
@@ 13115,6 +13113,14 @@
and `((u,v), (w,x))` is an edge iff either  `(u, w)` is an edge of self
and `v = x`, or  `(v, x)` is an edge of other and `u = w`.
+ .. SEEALSO::
+
+  :meth:`~sage.graphs.graph_decompositions.graph_products.is_cartesian_product`
+  factorization of graphs according to the cartesian product
+
+  :mod:`~sage.graphs.graph_decompositions.graph_products`
+  a module on graph products.
+
TESTS:
Cartesian product of graphs::
diff git a/sage/graphs/graph.py b/sage/graphs/graph.py
 a/sage/graphs/graph.py
+++ b/sage/graphs/graph.py
@@ 48,6 +48,7 @@
:meth:`~Graph.is_line_graph`  Tests wether the graph is a line graph.
:meth:`~Graph.is_odd_hole_free`  Tests whether ``self`` contains an induced odd hole.
:meth:`~Graph.is_even_hole_free`  Tests whether ``self`` contains an induced even hole.
+ :meth:`~Graph.is_cartesian_product`  Tests whether ``self`` is a cartesian product of graphs.
@@ 491,6 +492,7 @@
from sage.graphs.generic_graph import GenericGraph
from sage.graphs.digraph import DiGraph
+
class Graph(GenericGraph):
r"""
Undirected graph.
@@ 4707,6 +4709,55 @@
return D[0] == "Prime" and len(D[1]) == self.order()
+ def is_cartesian_product(self, certificate = False):
+ r"""
+ Tests whether ``self`` is a cartesian product of graphs.
+
+ INPUT:
+
+  ``certificate`` (boolean)  if ``certificate = False`` (default) the
+ method only returns ``True`` or ``False`` answers. If ``certificate =
+ True``, the ``True`` answers are replaced by the list of the factors of
+ the graph.
+
+ .. SEEALSO::
+
+  :meth:`~sage.graphs.generic_graph.GenericGraph.cartesian_product`
+
+  :mod:`~sage.graphs.graph_decompositions.graph_products`  a
+ module on graph products.
+
+ EXAMPLE:
+
+ The Petersen graph is prime::
+
+ sage: g = graphs.PetersenGraph()
+ sage: g.is_cartesian_product()
+ False
+
+ A 2d grid is the product of paths::
+
+ sage: g = graphs.Grid2dGraph(5,5)
+ sage: p1, p2 = g.is_cartesian_product(certificate = True)
+ sage: p1.is_isomorphic(graphs.PathGraph(5))
+ True
+ sage: p2.is_isomorphic(graphs.PathGraph(5))
+ True
+
+ And of course, we find the factors back when we build a graph from a
+ product::
+
+ sage: g = graphs.PetersenGraph().cartesian_product(graphs.CycleGraph(3))
+ sage: g1, g2 = g.is_cartesian_product(certificate = True)
+ sage: any( x.is_isomorphic(graphs.PetersenGraph()) for x in [g1,g2])
+ True
+ sage: any( x.is_isomorphic(graphs.CycleGraph(3)) for x in [g1,g2])
+ True
+ """
+ from sage.graphs.graph_decompositions.graph_products import is_cartesian_product
+ return is_cartesian_product(self, certificate = certificate)
+
+
def _gomory_hu_tree(self, vertices=None, method="FF"):
r"""
Returns a GomoryHu tree associated to self.
diff git a/sage/graphs/graph_decompositions/graph_products.pyx b/sage/graphs/graph_decompositions/graph_products.pyx
new file mode 100644
 /dev/null
+++ b/sage/graphs/graph_decompositions/graph_products.pyx
@@ 0,0 +1,286 @@
+r"""
+Products of graphs
+
+This module gathers everything related to graph products. At the moment it
+contains different implementations of recognition algorithms for graphs that can
+be written as a cartesian product of smaller ones.
+
+References:
+
+ .. [HIK11] Handbook of Product Graphs,
+ R. Hammack, W. Imrich, S. Klavzar,
+ CRC press, 2011
+
+Author:
+
+ Nathann Cohen (May 2012  coded while watching the election of Francois
+ Hollande on TV)
+
+Cartesian product of graphs  the recognition problem
+
+
+First, a definition:
+
+ **Definition** The cartesian product of two graphs `G` and `H`, denoted
+ `G\square H`, is a graph defined on the pairs `(g, h)\in V(G)\times V(H)`.
+
+ Two elements `(g, h),(g', h')\in V(G\square H)` are adjacent in `G\square H`
+ if and only if :
+
+  `g=g'` and `hh'\in H`; or
+  `h=h'` and `gg'\in H`
+
+Two remarks follow :
+
+#. The cartesian product is commutative
+
+#. Any edge `uv` of a graph `G_1 \square \cdots \square G_k` can be given a color
+ `i` corresponding to the unique index `i` such that `u_i` and `v_i` differ.
+
+The problem that is of interest to us in the present module is the following:
+
+ **Recognition problem** Given a graph `G`, can we guess whether there exist
+ graphs `G_1, ..., G_k` such that `G=G_1\square \cdots \square G_k` ?
+
+This problem can actually be solved, and the resulting factorization is
+unique. What is explained below can be found in the book *Handbook of Product
+Graphs* [HIK11]_.
+
+Everything is actually based on a simple observation. Given a graph `G`, finding
+out whether `G` can be written as the product of several graphs can be attempted
+by trying to color its edges according to some rules. Indeed, if we are to color
+the edges of `G` in such a way that each color class represents a factor of `G`,
+we must ensure several things.
+
+ **Remark 1** In any cycle of `G` no color can appear exactly once.
+
+ Indeed, if only one edge `uv` of a cycle were labelled with color `i`, it
+ would mean that:
+
+ #. The only difference between `u` and `v` lies in their `i` th coordinate
+
+ #. It is possible to go from `u` to `v` by changing only coordinates
+ different from the `i` th
+
+ A contradiction indeed.
+
+ .. MATH::
+
+ \begin{picture}(200,200)(100,100)
+ \put(50,0){\circle*{5}}
+ \put(50,0){\circle*{5}}
+ \put(25,35){\circle*{5}}
+ \put(25,35){\circle*{5}}
+ \put(25,35){\circle*{5}}
+ \put(25,35){\circle*{5}}
+ \qbezier(50,0)(37.5, 17.5)(25,35)
+ \qbezier(50,0)(37.5, 17.5)(25,35)
+ \qbezier(50,0)(37.5, 17.5)(25,35)
+ \qbezier(50,0)(37.5, 17.5)(25,35)
+ \qbezier(25,35)(0,35)(25,35)
+ \linethickness{1.5pt}
+ \qbezier[10](25,35)(0,35)(25,35)
+ \put(0,0){\makebox(0,0){No way !!!}}
+ \end{picture}
+
+ That means that, for instance, the edges of a triangle necessarily have the
+ same color.
+
+ **Remark 2** If two consecutive edges `u_1u_2` and `u_2u_3` have different
+ colors, there necessarily exists a unique vertex `u_4` different from `u_2`
+ and incident to both `u_1` and `u_3`.
+
+ In this situation, opposed edges necessarily have the same colors because of
+ the previous remark.
+
+ .. MATH::
+
+ \begin{picture}(200,200)(100,100)
+ \put(50,0){\circle*{5}}
+ \put(50,0){\circle*{5}}
+ \put(0,50){\circle*{5}}
+ \put(0,50){\circle*{5}}
+ \qbezier(50,0)(25,25)(0,50)
+ \qbezier(50,0)(25,25)(0,50)
+ \linethickness{1.5pt}
+ \qbezier[10](0,50)(25,25)(50,0)
+ \qbezier[10](0,50)(25,25)(50,0)
+ \put(0,55){\makebox(0,0)[b]{$u_3=(g',h')$}}
+ \put(0,55){\makebox(0,0)[t]{$u_1=(g,h)$}}
+ \put(55,0){\makebox(0,0)[l]{$u_4=(g,h')$}}
+ \put(55,0){\makebox(0,0)[r]{$u_2=(g',h)$}}
+ \end{picture}
+
+ As a corollary, we also know that:
+
+ #. If two vertices `u,v` have a *unique* common neighbor `x`, then `ux` and
+ `uv` have the same color.
+
+ #. If two vertices `u, v` have more that two common neighbors `x_1, ...,
+ x_k` then all edges between the `x_i` and the vertices of `u,v` have the
+ same color. This is also a consequence of the first remark.
+
+The algorithm
+^^^^^^^^^^^^^
+
+The previous remarks tell us that some edges are in some way equivalent to some
+others, i.e. that their colors are equal. In order to compute the coloring we
+are looking for, we therefore build a graph on the *edges* of a graph `G`,
+linking two edges whenever they are found to be equivalent according to the
+previous remarks.
+
+All that is left to do is to compute the connected components of this new graph,
+as each of them representing the edges of a factor. Of course, only one
+connected component indicates that the graph has no factorization.
+
+Then again, please refer to [HIK11]_ for any technical question.
+
+Methods
+
+"""
+
+#******************************************************************************
+# Copyright (C) 2012 Nathann Cohen *
+# *
+# Distributed under the terms of the GNU General Public License (GPL)*
+# http://www.gnu.org/licenses/ *
+#******************************************************************************
+
+def is_cartesian_product(g, certificate = False):
+ r"""
+ Tests whether the graph can be factorized.
+
+ INPUT:
+
+  ``certificate`` (boolean)  if ``certificate = False`` (default) the
+ method only returns ``True`` or ``False`` answers. If ``certificate =
+ True``, the ``True`` answers are replaced by the list of the factors of
+ the graph.
+
+ .. SEEALSO::
+
+  :meth:`~sage.graphs.generic_graph.GenericGraph.cartesian_product`
+
+ EXAMPLE:
+
+ The Petersen graph is prime::
+
+ sage: from sage.graphs.graph_decompositions.graph_products import is_cartesian_product
+ sage: g = graphs.PetersenGraph()
+ sage: is_cartesian_product(g)
+ False
+
+ A 2d grid is the product of paths::
+
+ sage: g = graphs.Grid2dGraph(5,5)
+ sage: p1, p2 = is_cartesian_product(g, certificate = True)
+ sage: p1.is_isomorphic(graphs.PathGraph(5))
+ True
+ sage: p2.is_isomorphic(graphs.PathGraph(5))
+ True
+
+ And of course, we find the factors back when we build a graph from a
+ product::
+
+ sage: g = graphs.PetersenGraph().cartesian_product(graphs.CycleGraph(3))
+ sage: g1, g2 = is_cartesian_product(g, certificate = True)
+ sage: any( x.is_isomorphic(graphs.PetersenGraph()) for x in [g1,g2])
+ True
+ sage: any( x.is_isomorphic(graphs.CycleGraph(3)) for x in [g1,g2])
+ True
+ """
+ from sage.rings.integer import Integer
+ H = g
+
+ # Of course the number of vertices of g can not be prime !
+ if Integer(g.order()).is_prime():
+ return False
+ if not g.is_connected():
+ raise ValueError("The graph must be connected !")
+
+ from sage.graphs.graph import Graph
+
+ # As we need the vertices of g to be linearly ordered, we copy the graph and
+ # relabel it
+ g = g.copy()
+ trev = g.relabel(return_map = True)
+ t = dict([(x,y) for y,x in trev.iteritems()])
+
+ # Reorder the vertices of an edge
+ r = lambda x,y : (x,y) if x