| 1900 | def minor(self, H, **kwds): |
| 1901 | r""" |
| 1902 | Returns the vertices of a minor isomorphic to `H` in the current graph. |
| 1903 | |
| 1904 | We say that a graph `G` has a `H`-minor (or that it has |
| 1905 | a graph isomorphic to `H` as a minor), if for all `h\in H`, |
| 1906 | there exist disjoint sets `S_h \subseteq V(G)` such that |
| 1907 | once the vertices of each `S_h` have been merged to create |
| 1908 | a new graph `G'`, this new graph contains `H` as a subgraph. |
| 1909 | |
| 1910 | For more information of minor theory, see |
| 1911 | http://en.wikipedia.org/wiki/Minor_(graph_theory) |
| 1912 | |
| 1913 | INPUT: |
| 1914 | |
| 1915 | - ``H`` -- The minor to find for in the current graph |
| 1916 | |
| 1917 | - ``**kwds`` -- arguments to be passed down to the ``solve`` |
| 1918 | function of ``MixedIntegerLinearProgram``. See the documentation |
| 1919 | of ``MixedIntegerLinearProgram.solve`` for more informations. |
| 1920 | |
| 1921 | OUTPUT: |
| 1922 | |
| 1923 | A dictionary associating to each vertex of `H` the set of vertices |
| 1924 | in the current graph representing it. |
| 1925 | |
| 1926 | ALGORITHM: |
| 1927 | |
| 1928 | Mixed Integer Linear Programming |
| 1929 | |
| 1930 | COMPLEXITY: |
| 1931 | |
| 1932 | Theoretically, when `H` is fixed, testing for the existence of |
| 1933 | a `H`-minor is polynomial. The known algorithms are highly |
| 1934 | exponential in `H`, though. |
| 1935 | |
| 1936 | NOTE: |
| 1937 | |
| 1938 | This function can be expected to be *very* slow, especially |
| 1939 | where the minor does not exist. |
| 1940 | |
| 1941 | EXAMPLE: |
| 1942 | |
| 1943 | Trying to find a minor isomorphic to `K_4` in |
| 1944 | the `4\times 4` grid :: |
| 1945 | |
| 1946 | sage: g = graphs.GridGraph([4,4]) |
| 1947 | sage: h = graphs.CompleteGraph(4) |
| 1948 | sage: L = g.minor(h) # optional - requires GLPK, CPLEX or CBC |
| 1949 | sage: gg = g.subgraph(flatten(L.values(), max_level = 1)) # optional - requires GLPK, CPLEX or CBC |
| 1950 | sage: _ = [gg.merge_vertices(l) for l in L.values() if len(l)>1] # optional - requires GLPK, CPLEX or CBC |
| 1951 | sage: gg.is_isomorphic(h) # optional - requires GLPK, CPLEX or CBC |
| 1952 | True |
| 1953 | |
| 1954 | We can also try to prove this way that the Petersen graph |
| 1955 | is not planar, as it has a `K_5` minor :: |
| 1956 | |
| 1957 | sage: g = graphs.PetersenGraph() |
| 1958 | sage: K5_minor = g.minor(graphs.CompleteGraph(5)) # optional long - requires GLPK, CPLEX or CBC |
| 1959 | |
| 1960 | And even a `K_{3,3}` minor :: |
| 1961 | |
| 1962 | sage: K33_minor = g.minor(graphs.CompleteBipartiteGraph(3,3)) # optional long - requires GLPK, CPLEX or CBC |
| 1963 | |
| 1964 | (It is much faster to use the linear-time test of |
| 1965 | planarity in this situation, though) |
| 1966 | |
| 1967 | As there is no cycle in a tree, looking for a `K_3` minor is useless. |
| 1968 | This function will raise an exception in this case:: |
| 1969 | |
| 1970 | sage: g = graphs.RandomGNP(20,.5) |
| 1971 | sage: g = g.subgraph(edges = g.min_spanning_tree()) |
| 1972 | sage: g.is_tree() |
| 1973 | True |
| 1974 | sage: L = g.minor(graphs.CompleteGraph(3)) # optional - requires GLPK, CPLEX or CBC |
| 1975 | Traceback (most recent call last): |
| 1976 | ... |
| 1977 | ValueError: This graph has no minor isomorphic to H ! |
| 1978 | """ |
| 1979 | |
| 1980 | from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException |
| 1981 | p = MixedIntegerLinearProgram() |
| 1982 | |
| 1983 | # sorts an edge |
| 1984 | S = lambda (x,y) : (x,y) if x<y else (y,x) |
| 1985 | |
| 1986 | # rs = Representative set of a vertex |
| 1987 | # for h in H, v in G is such that rs[h][v] == 1 if and only if v |
| 1988 | # is a representant of h in self |
| 1989 | rs = p.new_variable(dim=2) |
| 1990 | |
| 1991 | for v in self: |
| 1992 | p.add_constraint(sum([rs[h][v] for h in H]), max = 1) |
| 1993 | |
| 1994 | # We ensure that the set of representatives of a |
| 1995 | # vertex h contains a tree, and thus is connected |
| 1996 | |
| 1997 | # edges represents the edges of the tree |
| 1998 | edges = p.new_variable(dim = 2) |
| 1999 | |
| 2000 | # there can be a edge for h between two vertices |
| 2001 | # only if those vertices represent h |
| 2002 | for u,v in self.edges(labels=None): |
| 2003 | for h in H: |
| 2004 | p.add_constraint(edges[h][S((u,v))] - rs[h][u], max = 0 ) |
| 2005 | p.add_constraint(edges[h][S((u,v))] - rs[h][v], max = 0 ) |
| 2006 | |
| 2007 | # The number of edges of the tree in h is exactly the cardinal |
| 2008 | # of its representative set minus 1 |
| 2009 | |
| 2010 | for h in H: |
| 2011 | p.add_constraint(sum([edges[h][S(e)] for e in self.edges(labels=None)])-sum([rs[h][v] for v in self]), min=-1, max=-1) |
| 2012 | |
| 2013 | # a tree has no cycle |
| 2014 | epsilon = 1/(5*Integer(self.order())) |
| 2015 | r_edges = p.new_variable(dim=2) |
| 2016 | |
| 2017 | for h in H: |
| 2018 | for u,v in self.edges(labels=None): |
| 2019 | p.add_constraint(r_edges[h][(u,v)] + r_edges[h][(v,u)] - edges[h][S((u,v))], min = 0) |
| 2020 | |
| 2021 | for v in self: |
| 2022 | p.add_constraint(sum([r_edges[h][(u,v)] for u in self.neighbors(v)]), max = 1-epsilon) |
| 2023 | |
| 2024 | # Once the representative sets are described, we must ensure |
| 2025 | # there are arcs corresponding to those of H between them |
| 2026 | h_edges = p.new_variable(dim=2) |
| 2027 | |
| 2028 | for h1, h2 in H.edges(labels=None): |
| 2029 | |
| 2030 | for v1, v2 in self.edges(labels=None): |
| 2031 | |
| 2032 | p.add_constraint(h_edges[(h1,h2)][S((v1,v2))] - rs[h2][v2], max = 0) |
| 2033 | p.add_constraint(h_edges[(h1,h2)][S((v1,v2))] - rs[h1][v1], max = 0) |
| 2034 | |
| 2035 | p.add_constraint(h_edges[(h2,h1)][S((v1,v2))] - rs[h1][v2], max = 0) |
| 2036 | p.add_constraint(h_edges[(h2,h1)][S((v1,v2))] - rs[h2][v1], max = 0) |
| 2037 | |
| 2038 | p.add_constraint(sum([h_edges[(h1,h2)][S(e)] + h_edges[(h2,h1)][S(e)] for e in self.edges(labels=None) ]), min = 1) |
| 2039 | |
| 2040 | p.set_binary(rs) |
| 2041 | p.set_binary(edges) |
| 2042 | |
| 2043 | p.set_objective(None) |
| 2044 | |
| 2045 | try: |
| 2046 | p.solve(**kwds) |
| 2047 | except MIPSolverException: |
| 2048 | raise ValueError("This graph has no minor isomorphic to H !") |
| 2049 | |
| 2050 | rs = p.get_values(rs) |
| 2051 | |
| 2052 | from sage.sets.set import Set |
| 2053 | rs_dict = {} |
| 2054 | for h in H: |
| 2055 | rs_dict[h] = [v for v in self if rs[h][v]==1] |
| 2056 | |
| 2057 | return rs_dict |
| 2058 | |
| 2059 | |