@@ -4891,16 +4891,23 @@ def independent_set_of_representatives(self, family, solver=None, verbose=0,
4891
4891
return repr
4892
4892
4893
4893
@doc_index ("Algorithmically hard stuff" )
4894
- def minor (self , H , solver = None , verbose = 0 , * , integrality_tolerance = 1e-3 ):
4894
+ def minor (self , H , solver = None , verbose = 0 , * , integrality_tolerance = 1e-3 , induced = False ):
4895
4895
r"""
4896
- Return the vertices of a minor isomorphic to `H` in the current graph.
4896
+ Return the vertices of a minor isomorphic to `H` in the current graph for induced=False
4897
4897
4898
4898
We say that a graph `G` has a `H`-minor (or that it has a graph
4899
4899
isomorphic to `H` as a minor), if for all `h\in H`, there exist disjoint
4900
4900
sets `S_h \subseteq V(G)` such that once the vertices of each `S_h` have
4901
4901
been merged to create a new graph `G'`, this new graph contains `H` as a
4902
4902
subgraph.
4903
4903
4904
+ Returns an induced minor isomorphic to `H` if it exists for induced=True
4905
+
4906
+ We say that a graph `G` has an induced `H`-minor (or that it has a
4907
+ graph isomorphic to `H` as an induced minor), if `H` can be obtained
4908
+ from an induced subgraph of `G` by contracting edges. Otherwise, `G` is
4909
+ said to be `H`-induced minor-free.
4910
+
4904
4911
For more information, see the :wikipedia:`Minor_(graph_theory)`.
4905
4912
4906
4913
INPUT:
@@ -4922,6 +4929,9 @@ def minor(self, H, solver=None, verbose=0, *, integrality_tolerance=1e-3):
4922
4929
solvers over an inexact base ring; see
4923
4930
:meth:`MixedIntegerLinearProgram.get_values`.
4924
4931
4932
+ - ``induced`` -- boolean (default: ``False``); if ``True``, returns an
4933
+ induced minor isomorphic to `H` if it exists, and ``None`` otherwise.
4934
+
4925
4935
OUTPUT:
4926
4936
4927
4937
A dictionary associating to each vertex of `H` the set of vertices in
@@ -4979,149 +4989,6 @@ def minor(self, H, solver=None, verbose=0, *, integrality_tolerance=1e-3):
4979
4989
Traceback (most recent call last):
4980
4990
...
4981
4991
ValueError: This graph has no minor isomorphic to H !
4982
- """
4983
- self ._scream_if_not_simple ()
4984
- H ._scream_if_not_simple ()
4985
- from sage .numerical .mip import MixedIntegerLinearProgram , MIPSolverException
4986
- p = MixedIntegerLinearProgram (solver = solver )
4987
-
4988
- # We use frozenset((u, v)) to avoid confusion between (u, v) and (v, u)
4989
-
4990
- # rs = Representative set of a vertex
4991
- # for h in H, v in G is such that rs[h,v] == 1 if and only if v
4992
- # is a representative of h in self
4993
- rs = p .new_variable (binary = True )
4994
-
4995
- for v in self :
4996
- p .add_constraint (p .sum (rs [h , v ] for h in H ), max = 1 )
4997
-
4998
- # We ensure that the set of representatives of a
4999
- # vertex h contains a tree, and thus is connected
5000
-
5001
- # edges represents the edges of the tree
5002
- edges = p .new_variable (binary = True )
5003
-
5004
- # there can be a edge for h between two vertices
5005
- # only if those vertices represent h
5006
- for u , v in self .edge_iterator (labels = None ):
5007
- fuv = frozenset ((u , v ))
5008
- for h in H :
5009
- p .add_constraint (edges [h , fuv ] - rs [h , u ], max = 0 )
5010
- p .add_constraint (edges [h , fuv ] - rs [h , v ], max = 0 )
5011
-
5012
- # The number of edges of the tree in h is exactly the cardinal
5013
- # of its representative set minus 1
5014
-
5015
- for h in H :
5016
- p .add_constraint (p .sum (edges [h , frozenset (e )] for e in self .edge_iterator (labels = None ))
5017
- - p .sum (rs [h , v ] for v in self ), min = - 1 , max = - 1 )
5018
-
5019
- # a tree has no cycle
5020
- epsilon = 1 / (5 * Integer (self .order ()))
5021
- r_edges = p .new_variable (nonnegative = True )
5022
-
5023
- for h in H :
5024
- for u , v in self .edge_iterator (labels = None ):
5025
- p .add_constraint (r_edges [h , (u , v )] + r_edges [h , (v , u )] - edges [h , frozenset ((u , v ))], min = 0 )
5026
-
5027
- for v in self :
5028
- p .add_constraint (p .sum (r_edges [h , (u , v )] for u in self .neighbor_iterator (v )), max = 1 - epsilon )
5029
-
5030
- # Once the representative sets are described, we must ensure
5031
- # there are arcs corresponding to those of H between them
5032
- h_edges = p .new_variable (nonnegative = True )
5033
-
5034
- for h1 , h2 in H .edge_iterator (labels = None ):
5035
-
5036
- for v1 , v2 in self .edge_iterator (labels = None ):
5037
- fv1v2 = frozenset ((v1 , v2 ))
5038
- p .add_constraint (h_edges [(h1 , h2 ), fv1v2 ] - rs [h2 , v2 ], max = 0 )
5039
- p .add_constraint (h_edges [(h1 , h2 ), fv1v2 ] - rs [h1 , v1 ], max = 0 )
5040
-
5041
- p .add_constraint (h_edges [(h2 , h1 ), fv1v2 ] - rs [h1 , v2 ], max = 0 )
5042
- p .add_constraint (h_edges [(h2 , h1 ), fv1v2 ] - rs [h2 , v1 ], max = 0 )
5043
-
5044
- p .add_constraint (p .sum (h_edges [(h1 , h2 ), frozenset (e )] + h_edges [(h2 , h1 ), frozenset (e )]
5045
- for e in self .edge_iterator (labels = None )), min = 1 )
5046
-
5047
- p .set_objective (None )
5048
-
5049
- try :
5050
- p .solve (log = verbose )
5051
- except MIPSolverException :
5052
- raise ValueError ("This graph has no minor isomorphic to H !" )
5053
-
5054
- rs = p .get_values (rs , convert = bool , tolerance = integrality_tolerance )
5055
-
5056
- rs_dict = {}
5057
- for h in H :
5058
- rs_dict [h ] = [v for v in self if rs [h , v ]]
5059
-
5060
- return rs_dict
5061
-
5062
- def induced_minor (self , H , solver = None , verbose = 0 , integrality_tolerance = 1e-10 ):
5063
- r"""
5064
- Returns an induced minor isomorphic to `H` if it exists.
5065
-
5066
- We say that a graph `G` has an induced `H`-minor (or that it has a
5067
- graph isomorphic to `H` as an induced minor), if `H` can be obtained
5068
- from an induced subgraph of `G` by contracting edges. Otherwise, `G` is
5069
- said to be `H`-induced minor-free.
5070
-
5071
- For more information, see the :wikipedia:`Minor_(graph_theory)`.
5072
-
5073
- INPUT:
5074
-
5075
- - ``H`` -- The induced minor to find for in the current graph.
5076
-
5077
- - ``solver`` -- string (default: ``None``); specify a Mixed Integer
5078
- Linear Programming (MILP) solver to be used. If set to ``None``, the
5079
- default one is used. For more information on MILP solvers and which
5080
- default solver is used, see the method :meth:`solve
5081
- <sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the class
5082
- :class:`MixedIntegerLinearProgram
5083
- <sage.numerical.mip.MixedIntegerLinearProgram>`.
5084
-
5085
- - ``verbose`` -- integer (default: ``0``); sets the level of
5086
- verbosity. Set to 0 by default, which means quiet.
5087
-
5088
- - ``integrality_tolerance`` -- float; parameter for use with MILP
5089
- solvers over an inexact base ring; see
5090
- :meth:`MixedIntegerLinearProgram.get_values`.
5091
-
5092
- OUTPUT:
5093
-
5094
- A dictionary associating to each vertex of `H` the set of vertices in
5095
- the current graph representing it.
5096
-
5097
- ALGORITHM:
5098
-
5099
- Mixed Integer Linear Programming (MILP) is used to find the minor. The
5100
- algorithm is described in [KKT2006]_.
5101
-
5102
- COMPLEXITY:
5103
-
5104
- Theoretically, when `H` is fixed, testing for the existence of a induced
5105
- minor of `H` is polynomial. The known algorithms are highly exponential in
5106
- `H`, though.
5107
-
5108
- .. NOTE::
5109
-
5110
- This function can be expected to be *very* slow, especially where
5111
- the induced minor does not exist.
5112
-
5113
- EXAMPLES:
5114
-
5115
- Trying to find an induced minor isomorphic to `K_4` in the `4\times 4` grid::
5116
-
5117
- sage: # needs sage.numerical.mip
5118
- sage: g = graphs.GridGraph([4,4])
5119
- sage: h = graphs.CompleteGraph(4)
5120
- sage: L = g.induced_minor(h)
5121
- sage: gg = g.subgraph(flatten(L.values(), max_level = 1))
5122
- sage: _ = [gg.merge_vertices(l) for l in L.values() if len(l)>1]
5123
- sage: gg.is_isomorphic(h)
5124
- True
5125
4992
5126
4993
Trying to find an induced minor for a graph with a C6 cycle::
5127
4994
@@ -5136,7 +5003,7 @@ def induced_minor(self, H, solver=None, verbose=0, integrality_tolerance=1e-10):
5136
5003
....: random_cycle_vertex = random.choice(cycle_vertices)
5137
5004
....: g.add_edge(random_cycle_vertex, vertex)
5138
5005
sage: h = Graph([(i, (i + 1) % 5) for i in range(5)]) # Create a graph with 5 vertices forming a C5 cycle
5139
- sage: L = g.induced_minor(h )
5006
+ sage: L = g.minor(h, induced=True) )
5140
5007
sage: gg = g.subgraph(flatten(L.values(), max_level = 1))
5141
5008
sage: _ = [gg.merge_vertices(l) for l in L.values() if len(l)>1]
5142
5009
sage: gg.is_isomorphic(h)
@@ -5148,7 +5015,7 @@ def induced_minor(self, H, solver=None, verbose=0, integrality_tolerance=1e-10):
5148
5015
sage: g.add_edges([(0, 1), (0, 2), (1, 2), (2, 3), (3, 4), (3, 5), (4, 5), (6, 5)])
5149
5016
sage: h = Graph()
5150
5017
sage: h.add_edges([(9, 10), (9, 11), (9, 12), (9, 13)])
5151
- sage: l = g.induced_minor(h )
5018
+ sage: l = g.minor(h, induced=True )
5152
5019
Traceback (most recent call last):
5153
5020
...
5154
5021
ValueError: This graph has no induced minor isomorphic to H !
@@ -5157,12 +5024,11 @@ def induced_minor(self, H, solver=None, verbose=0, integrality_tolerance=1e-10):
5157
5024
sage: g.add_edges([(0, 1), (0, 2), (1, 2), (2, 3), (3, 4), (3, 5), (4, 5), (6, 5)])
5158
5025
sage: h = Graph()
5159
5026
sage: h.add_edges([(7, 8), (8, 9), (9, 10), (10, 11)])
5160
- sage: L = g.induced_minor(h )
5027
+ sage: L = g.minor(h, induced=True )
5161
5028
sage: gg = g.subgraph(flatten(L.values(), max_level = 1))
5162
5029
sage: _ = [gg.merge_vertices(l) for l in L.values() if len(l)>1]
5163
5030
sage: gg.is_isomorphic(h)
5164
5031
True
5165
-
5166
5032
"""
5167
5033
self ._scream_if_not_simple ()
5168
5034
H ._scream_if_not_simple ()
@@ -5227,27 +5093,26 @@ def induced_minor(self, H, solver=None, verbose=0, integrality_tolerance=1e-10):
5227
5093
5228
5094
p .add_constraint (p .sum (h_edges [(h1 , h2 ), frozenset (e )] + h_edges [(h2 , h1 ), frozenset (e )]
5229
5095
for e in self .edge_iterator (labels = None )), min = 1 )
5230
-
5231
-
5096
+
5097
+ # if induced is True
5232
5098
# condition for induced subgraph ensures that if there
5233
5099
# doesnt exist an edge(h1, h2) in H then there should
5234
5100
# not be an edge between representative sets of h1 and h2 in G
5235
- for h1 in H :
5236
- for h2 in H :
5237
- if not h1 == h2 and not H .has_edge (h1 , h2 ):
5238
- for v1 , v2 in self .edge_iterator (labels = None ):
5239
- expr2 = rs [h1 , v1 ] + rs [h2 , v2 ]
5240
- p .add_constraint (expr2 , max = 1 )
5241
-
5101
+ if (induced ):
5102
+ for h1 in H :
5103
+ for h2 in H :
5104
+ if not h1 == h2 and not H .has_edge (h1 , h2 ):
5105
+ for v1 , v2 in self .edge_iterator (labels = None ):
5106
+ p .add_constraint (rs [h1 , v1 ] + rs [h2 , v2 ], max = 1 )
5242
5107
5243
5108
p .set_objective (None )
5244
5109
5245
5110
try :
5246
5111
p .solve (log = verbose )
5247
5112
except MIPSolverException :
5248
- raise ValueError ("This graph has no induced minor isomorphic to H !" )
5113
+ raise ValueError ("This graph has no minor isomorphic to H !" )
5249
5114
5250
- rs = p .get_values (rs )
5115
+ rs = p .get_values (rs , convert = bool , tolerance = integrality_tolerance )
5251
5116
5252
5117
rs_dict = {}
5253
5118
for h in H :
0 commit comments