Skip to content

Commit de9da91

Browse files
author
Release Manager
committed
gh-36354: added induced minor function to graph algorithms ### Description Fixes #36353 This pull request introduces the "induced_minor" function to the SageMath library for graph theory. The function allows users to find an induced minor isomorphic to a given target graph `H` in a graph `G`. It utilizes Mixed Integer Linear Programming (MILP) to solve this problem efficiently. ### Motivation The "induced_minor" function is a valuable addition to SageMath as it addresses the need to find induced minors, a fundamental concept in graph theory. Users often encounter scenarios where they need to identify an induced minor isomorphic to a specific target graph, and this function streamlines the process. Issue: #36353 Documentation Issue: ---- ### Implementation #### Logic An existing minor function had several constraints for finding an induced minor isomorphic to a given target graph `H` in a graph`G: 1. Representative Sets: Ensure that each vertex `v` in `G` represents a vertex `h` in `H` using binary variables `rs[h, v]`. 2. Connected Representative Sets: Enforce that the representative sets in `G` form a connected tree structure using binary variables `edges[h, fuv]` for edges `(u, v)` in `G`. 3. Tree Structure: Ensure that the number of edges in the tree for each `h` in `H` is exactly one less than the cardinality of its representative set. 4. Acyclic Trees: Maintain acyclic tree structures for representative sets using `r_edges[h, (u, v)]` variables and constraints. 5. Edges Correspondence: Ensure that edges between representative sets in `G` correspond to edges in `H` using `h_edges` variables. For induced minor, we just had to add an additional constraint: 6. Induced Subgraph Condition: Checks for the existence of edges `(h1, h2)` in target graph `H`. If no such edge exists in `H`, it ensures that there is no edge between the representative sets of vertices `h1` and `h2` in graph `G`. #### Algorithm and Complexity The "induced_minor" function uses MILP to find the minor and follows the algorithm described in [KKT2006]. The complexity of this operation is discussed in the docstring. #### Example Usage The PR includes examples demonstrating how to use the "induced_minor" function to find induced minors isomorphic to specific target graphs in various scenarios. #### Tests The code includes tests to ensure the correctness of the "induced_minor" function. These tests cover different use cases, including scenarios where no induced minor exists. #### Documentation The documentation will be updated in a separate PR to include information about the new "induced_minor" function. ### 📝 Checklist - [x] The title is concise, informative, and self-explanatory. - [x] The description explains in detail what this PR is about. - [x] I have linked the relevant issue #36353. - [x] I have created tests covering the changes. - [x] I will update the documentation in a separate PR, if required. ### ⌛ Dependencies N/A (There are no open PRs that this PR depends on.) URL: #36354 Reported by: saatvikraoIITGN Reviewer(s): David Coudert, saatvikraoIITGN
2 parents 6ab9c83 + 226c97a commit de9da91

File tree

1 file changed

+64
-2
lines changed

1 file changed

+64
-2
lines changed

src/sage/graphs/graph.py

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4891,7 +4891,7 @@ 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, induced=False, *, integrality_tolerance=1e-3):
48954895
r"""
48964896
Return the vertices of a minor isomorphic to `H` in the current graph.
48974897
@@ -4901,6 +4901,14 @@ def minor(self, H, solver=None, verbose=0, *, integrality_tolerance=1e-3):
49014901
been merged to create a new graph `G'`, this new graph contains `H` as a
49024902
subgraph.
49034903
4904+
When parameter ``induced`` is ``True``, this method returns an induced minor
4905+
isomorphic to `H`, if it exists.
4906+
4907+
We say that a graph `G` has an induced `H`-minor (or that it has a
4908+
graph isomorphic to `H` as an induced minor), if `H` can be obtained
4909+
from an induced subgraph of `G` by contracting edges. Otherwise, `G` is
4910+
said to be `H`-induced minor-free.
4911+
49044912
For more information, see the :wikipedia:`Minor_(graph_theory)`.
49054913
49064914
INPUT:
@@ -4922,6 +4930,10 @@ def minor(self, H, solver=None, verbose=0, *, integrality_tolerance=1e-3):
49224930
solvers over an inexact base ring; see
49234931
:meth:`MixedIntegerLinearProgram.get_values`.
49244932
4933+
- ``induced`` -- boolean (default: ``False``); if ``True``, returns an
4934+
induced minor isomorphic to `H` if it exists, and raises a
4935+
:class:`ValueError` otherwise.
4936+
49254937
OUTPUT:
49264938
49274939
A dictionary associating to each vertex of `H` the set of vertices in
@@ -4979,6 +4991,43 @@ def minor(self, H, solver=None, verbose=0, *, integrality_tolerance=1e-3):
49794991
Traceback (most recent call last):
49804992
...
49814993
ValueError: This graph has no minor isomorphic to H !
4994+
4995+
Trying to find an induced minor isomorphic to `C_5` in a graph
4996+
containing an induced `C_6`::
4997+
4998+
sage: g = graphs.CycleGraph(6)
4999+
sage: for i in range(randint(10, 30)):
5000+
....: g.add_edge(randint(0, 5), g.add_vertex())
5001+
sage: h = graphs.CycleGraph(5)
5002+
sage: L = g.minor(h, induced=True)
5003+
sage: gg = g.subgraph(flatten(L.values(), max_level=1))
5004+
sage: _ = [gg.merge_vertices(l) for l in L.values() if len(l) > 1]
5005+
sage: gg.is_isomorphic(h)
5006+
True
5007+
5008+
TESTS::
5009+
5010+
A graph `g` may have a minor isomorphic to a given graph `h` but no
5011+
induced minor isomorphic to `h`::
5012+
5013+
sage: g = Graph([(0, 1), (0, 2), (1, 2), (2, 3), (3, 4), (3, 5), (4, 5), (6, 5)])
5014+
sage: h = Graph([(9, 10), (9, 11), (9, 12), (9, 13)])
5015+
sage: l = g.minor(h, induced=False)
5016+
sage: l = g.minor(h, induced=True)
5017+
Traceback (most recent call last):
5018+
...
5019+
ValueError: This graph has no induced minor isomorphic to H !
5020+
5021+
Checking that the returned induced minor is isomorphic to the given
5022+
graph::
5023+
5024+
sage: g = Graph([(0, 1), (0, 2), (1, 2), (2, 3), (3, 4), (3, 5), (4, 5), (6, 5)])
5025+
sage: h = Graph([(7, 8), (8, 9), (9, 10), (10, 11)])
5026+
sage: L = g.minor(h, induced=True)
5027+
sage: gg = g.subgraph(flatten(L.values(), max_level=1))
5028+
sage: _ = [gg.merge_vertices(l) for l in L.values() if len(l) > 1]
5029+
sage: gg.is_isomorphic(h)
5030+
True
49825031
"""
49835032
self._scream_if_not_simple()
49845033
H._scream_if_not_simple()
@@ -5044,12 +5093,25 @@ def minor(self, H, solver=None, verbose=0, *, integrality_tolerance=1e-3):
50445093
p.add_constraint(p.sum(h_edges[(h1, h2), frozenset(e)] + h_edges[(h2, h1), frozenset(e)]
50455094
for e in self.edge_iterator(labels=None)), min=1)
50465095

5096+
# if induced is True
5097+
# condition for induced subgraph ensures that if there
5098+
# doesnt exist an edge(h1, h2) in H then there should
5099+
# not be an edge between representative sets of h1 and h2 in G
5100+
if induced:
5101+
for h1, h2 in H.complement().edge_iterator(labels=False):
5102+
for v1, v2 in self.edge_iterator(labels=False):
5103+
p.add_constraint(rs[h1, v1] + rs[h2, v2], max=1)
5104+
p.add_constraint(rs[h2, v1] + rs[h1, v2], max=1)
5105+
50475106
p.set_objective(None)
50485107

50495108
try:
50505109
p.solve(log=verbose)
50515110
except MIPSolverException:
5052-
raise ValueError("This graph has no minor isomorphic to H !")
5111+
if induced:
5112+
raise ValueError("This graph has no induced minor isomorphic to H !")
5113+
else:
5114+
raise ValueError("This graph has no minor isomorphic to H !")
50535115

50545116
rs = p.get_values(rs, convert=bool, tolerance=integrality_tolerance)
50555117

0 commit comments

Comments
 (0)