Skip to content

Commit 49745af

Browse files
Saatvik RaosaatvikraoIITGN
authored andcommitted
unified the minor and induced minor functions
1 parent d8befd8 commit 49745af

File tree

1 file changed

+25
-160
lines changed

1 file changed

+25
-160
lines changed

src/sage/graphs/graph.py

Lines changed: 25 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -4891,16 +4891,23 @@ def independent_set_of_representatives(self, family, solver=None, verbose=0,
48914891
return repr
48924892

48934893
@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):
48954895
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
48974897
48984898
We say that a graph `G` has a `H`-minor (or that it has a graph
48994899
isomorphic to `H` as a minor), if for all `h\in H`, there exist disjoint
49004900
sets `S_h \subseteq V(G)` such that once the vertices of each `S_h` have
49014901
been merged to create a new graph `G'`, this new graph contains `H` as a
49024902
subgraph.
49034903
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+
49044911
For more information, see the :wikipedia:`Minor_(graph_theory)`.
49054912
49064913
INPUT:
@@ -4922,6 +4929,9 @@ def minor(self, H, solver=None, verbose=0, *, integrality_tolerance=1e-3):
49224929
solvers over an inexact base ring; see
49234930
:meth:`MixedIntegerLinearProgram.get_values`.
49244931
4932+
- ``induced`` -- boolean (default: ``False``); if ``True``, returns an
4933+
induced minor isomorphic to `H` if it exists, and ``None`` otherwise.
4934+
49254935
OUTPUT:
49264936
49274937
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):
49794989
Traceback (most recent call last):
49804990
...
49814991
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
51254992
51264993
Trying to find an induced minor for a graph with a C6 cycle::
51274994
@@ -5136,7 +5003,7 @@ def induced_minor(self, H, solver=None, verbose=0, integrality_tolerance=1e-10):
51365003
....: random_cycle_vertex = random.choice(cycle_vertices)
51375004
....: g.add_edge(random_cycle_vertex, vertex)
51385005
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))
51405007
sage: gg = g.subgraph(flatten(L.values(), max_level = 1))
51415008
sage: _ = [gg.merge_vertices(l) for l in L.values() if len(l)>1]
51425009
sage: gg.is_isomorphic(h)
@@ -5148,7 +5015,7 @@ def induced_minor(self, H, solver=None, verbose=0, integrality_tolerance=1e-10):
51485015
sage: g.add_edges([(0, 1), (0, 2), (1, 2), (2, 3), (3, 4), (3, 5), (4, 5), (6, 5)])
51495016
sage: h = Graph()
51505017
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)
51525019
Traceback (most recent call last):
51535020
...
51545021
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):
51575024
sage: g.add_edges([(0, 1), (0, 2), (1, 2), (2, 3), (3, 4), (3, 5), (4, 5), (6, 5)])
51585025
sage: h = Graph()
51595026
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)
51615028
sage: gg = g.subgraph(flatten(L.values(), max_level = 1))
51625029
sage: _ = [gg.merge_vertices(l) for l in L.values() if len(l)>1]
51635030
sage: gg.is_isomorphic(h)
51645031
True
5165-
51665032
"""
51675033
self._scream_if_not_simple()
51685034
H._scream_if_not_simple()
@@ -5227,27 +5093,26 @@ def induced_minor(self, H, solver=None, verbose=0, integrality_tolerance=1e-10):
52275093

52285094
p.add_constraint(p.sum(h_edges[(h1, h2), frozenset(e)] + h_edges[(h2, h1), frozenset(e)]
52295095
for e in self.edge_iterator(labels=None)), min=1)
5230-
5231-
5096+
5097+
# if induced is True
52325098
# condition for induced subgraph ensures that if there
52335099
# doesnt exist an edge(h1, h2) in H then there should
52345100
# 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)
52425107

52435108
p.set_objective(None)
52445109

52455110
try:
52465111
p.solve(log=verbose)
52475112
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 !")
52495114

5250-
rs = p.get_values(rs)
5115+
rs = p.get_values(rs, convert=bool, tolerance=integrality_tolerance)
52515116

52525117
rs_dict = {}
52535118
for h in H:

0 commit comments

Comments
 (0)