Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
sagemathgh-38892: Implemented methods pertaining to the canonical partition of a matching covered graph
    
<!-- ^ Please provide a concise and informative title. -->
The objective of this issue is to implement the methods pertaining to
the canonical partition of a matching covered graph.

<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
More specifically, this PR aims to implement the following two methods:
- [x] `maximal_barrier()`: Return the (unique) maximal barrier
containing the provided vertex.
- [x] `canonical_partition()`: Return the canonical partition of the
(matching covered) graph.

<!-- v Why is this change required? What problem does it solve? -->
This PR shall address the methods related to canonical partition of
matching covered graphs.

<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->
Fixes sagemath#38216. Note that this issue fixes a small part of the mentioned
issue.

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies
This PR depends on the PR sagemath#38742.

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->

cc: @dcoudert.
    
URL: sagemath#38892
Reported by: Janmenjaya Panda
Reviewer(s): David Coudert, Janmenjaya Panda
  • Loading branch information
Release Manager committed Dec 8, 2024
2 parents 9f18f47 + ee8bdbd commit 6ef076b
Showing 1 changed file with 267 additions and 10 deletions.
277 changes: 267 additions & 10 deletions src/sage/graphs/matching_covered_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,6 @@
``transitive_reduction()`` | Return a transitive reduction of the matching covered graph.
``union()`` | Return the union of ``self`` and ``other``.
**Barriers and canonical partition**
.. csv-table::
:class: contentstable
:widths: 30, 70
:delim: |
``canonical_partition()`` | Return the canonical partition of the (matching covered) graph.
``maximal_barrier()`` | Return the (unique) maximal barrier of the (matching covered) graph containing the (provided) vertex.
**Bricks, braces and tight cut decomposition**
.. csv-table::
Expand Down Expand Up @@ -165,6 +155,7 @@
from sage.graphs.graph import Graph
from sage.misc.rest_index_of_methods import doc_index, gen_thematic_rest_table_index


class MatchingCoveredGraph(Graph):
r"""
Matching covered graph
Expand Down Expand Up @@ -1661,6 +1652,84 @@ def allows_loops(self):
"""
return False

@doc_index('Barriers and canonical partition')
def canonical_partition(self):
r"""
Return the canonical partition of the (matching covered) graph.
For a matching covered graph `G`, a subset `B` of the vertex set `V` is
a barrier if `|B| = o(G - B)`, where `|B|` denotes the cardinality of
the set `B` and `o(G - B)` denotes the number of odd components in the
graph `G - B`. And a barrier `B` is a maximal barrier if `C` is not a
barrier for each `C` such that `B \subset C \subseteq V`.
Note that in a matching covered graph, each vertex belongs to a unique
maximal barrier. The maximal barriers of a matching covered graph
partitions its vertex set and the partition of the vertex set of a
matching covered graph into its maximal barriers is called as its
*canonical* *partition*.
OUTPUT:
- A list of sets that constitute a (canonical) partition of the vertex
set, wherein each set is a (unique) maximal barrier of the (matching
covered) graph.
EXAMPLES:
Show the maximal barrier of the graph `K_4 \odot K_{3, 3}`::
sage: G = Graph([
....: (0, 2), (0, 3), (0, 4), (1, 2),
....: (1, 3), (1, 4), (2, 5), (3, 6),
....: (4, 7), (5, 6), (5, 7), (6, 7)
....: ])
sage: H = MatchingCoveredGraph(G)
sage: H.canonical_partition()
[{0}, {1}, {2, 3, 4}, {5}, {6}, {7}]
For a bicritical graph (for instance, the Petersen graph), the
canonical parition constitutes of only singleton sets each containing
an individual vertex::
sage: P = graphs.PetersenGraph()
sage: G = MatchingCoveredGraph(P)
sage: G.canonical_partition()
[{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}]
For a bipartite matching covered graph (for instance, the Hexahedral
graph), the canonical partition consists of two sets each of which
corresponds to the individual color class::
sage: H = graphs.HexahedralGraph()
sage: G = MatchingCoveredGraph(H)
sage: G.canonical_partition()
[{0, 2, 5, 7}, {1, 3, 4, 6}]
sage: B = BipartiteGraph(H)
sage: list(B.bipartition()) == G.canonical_partition()
True
REFERENCES:
- [LM2024]_
.. SEEALSO::
- :meth:`~sage.graphs.graph.Graph.is_bicritical`
- :meth:`~sage.graphs.graph.Graph.is_matching_covered`
- :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.maximal_barrier`
"""
visited = set()

maximal_barriers = []
for v in self:
if v not in visited:
B = self.maximal_barrier(v)
visited.update(B)
maximal_barriers.append(B)

return maximal_barriers

@doc_index('Overwritten methods')
def delete_vertex(self, vertex, in_order=False):
r"""
Expand Down Expand Up @@ -1918,6 +1987,194 @@ def get_matching(self):
"""
return self._matching

@doc_index('Barriers and canonical partition')
def maximal_barrier(self, vertex):
r"""
Return the (unique) maximal barrier containing the vertex.
For a matching covered graph `G`, a subset `B` of the vertex set `V` is
a barrier if `|B| = o(G - B)`, where `|B|` denotes the cardinality of
the set `B` and `o(G - B)` denotes the number of odd components in the
graph `G - B`. And a barrier `B` is a maximal barrier if `C` is not a
barrier for each `C` such that `B \subset C \subseteq V`.
In a matching covered graph, each vertex belongs to a unique maximal
barrier, which is a consequence of the following theorem.
.. RUBRIC:: Theorem [LM2024]_:
Let `u` and `v` be any two vertices in a matchable graph `G`. Then the
graph `G - u - v` is matchable if and only if there is no barrier of
`G` which contains both `u` and `v`.
And in order to find the vertices that do not lie in the maximal
barrier containing the provided vertex in linear time we take
inspiration of the `M` alternating tree seach method [LR2004]_.
INPUT:
- ``vertex`` -- a vertex of the graph
OUTPUT:
- A :exc:`~ValueError` is returned if ``vertex`` is not a vertex of the
graph, otherwise a set of vertices that constitute the (unique)
maximal barrier containing the vertex is returned.
EXAMPLES:
The graph `K_4 \odot K_{3, 3}` is matching covered. Show the set of
vertices in the (unique) maximal barrier containing the vertex `2`::
sage: G = Graph([
....: (0, 2), (0, 3), (0, 4), (1, 2),
....: (1, 3), (1, 4), (2, 5), (3, 6),
....: (4, 7), (5, 6), (5, 7), (6, 7)
....: ])
sage: H = MatchingCoveredGraph(G)
sage: B = H.maximal_barrier(2)
sage: B
{2, 3, 4}
Let `B` be a maximal barrier of a matching covered graph `G` (which is,
of course, a matchable graph). The graph, `J := G - B` has no even
component::
sage: J = G.copy()
sage: J.delete_vertices(B)
sage: all(len(K)%2 != 0 for K in J.connected_components())
...
True
Let `B` be a maximal barrier in a matching covered graph `G` and let
`M` be a perfect matching of `G`. If `K` is an odd component of
`J := G - B`, then `M \cap \partial_G(K)` has precisely one edge and if
`v` is the end of that edge in `V(K)`, then `M \cap E(K)` is a perfect
matching of `K - v`::
sage: K = J.subgraph(vertices=(J.connected_components())[0])
sage: # Let F := \partial_G(K) and T := M \cap F
sage: F = [edge for edge in G.edge_iterator()
....: if (edge[0] in K and edge[1] not in K)
....: or (edge[0] not in K and edge[1] in K)
....: ]
sage: M = H.get_matching()
sage: T = [edge for edge in F if edge in M]
sage: len(T) == 1
True
sage: v = T[0][0] if T[0][0] in K else T[0][1]
sage: # Let N := M \cap E(K) and L := K - v
sage: N = Graph([edge for edge in K.edge_iterator() if edge in M])
sage: L = K.copy()
sage: L.delete_vertex(v)
sage: # Check if N is a perfect matching of L
sage: L.order() == 2*N.size()
True
Let `B` be a maximal barrier of a matching covered graph `G` (which is,
of course, a matchable graph). The graph induced by each component of
`G - B` is factor critical::
sage: all((K.subgraph(vertices=connected_component)).is_factor_critical()
....: for connected_component in K.connected_components()
....: )
True
For a bicritical graph (for instance, the Petersen graph), for each
vertex the maximal barrier is a singleton set containing only that
vertex::
sage: P = graphs.PetersenGraph()
sage: G = MatchingCoveredGraph(P)
sage: u = 0
sage: set([u]) == G.maximal_barrier(u)
True
In a bipartite matching covered graph (for instance, the Hexahedral
graph), for a vertex, the maximal barrier is the set of vertices of
the color class that the particular vertex belongs to. In other words,
there are precisely two maximal barriers in a bipartite matching
covered graph, that is, the vertex sets of the individual color class::
sage: G = graphs.HexahedralGraph()
sage: H = MatchingCoveredGraph(G)
sage: A, _ = H.bipartite_sets()
sage: # needs random
sage: import random
sage: a = random.choice(list(A))
sage: A == H.maximal_barrier(a)
True
Maximal barriers of matching covered graph constitute a partition of
its vertex set::
sage: S = set()
sage: for v in H:
....: B = tuple(sorted(list(H.maximal_barrier(v))))
....: S.add(B)
sage: S = list(S)
sage: # Check that S is a partition of the vertex set of H
sage: # Part 1: Check if S spans the vertex set of H
sage: sorted([u for B in S for u in B]) == sorted(list(H))
True
sage: # Part 2: Check if each maximal barrier in S is disjoint
sage: is_disjoint = True
sage: for i in range(len(S)):
....: for j in range(i+1, len(S)):
....: c = [v for v in S[i] if v in S[j]]
....: is_disjoint = (len(c) == 0)
sage: is_disjoint
True
TESTS:
Providing with a nonexistent vertex::
sage: P = graphs.PetersenGraph()
sage: G = MatchingCoveredGraph(P)
sage: G.maximal_barrier('')
Traceback (most recent call last):
...
ValueError: vertex not in the graph
sage: G.maximal_barrier(100)
Traceback (most recent call last):
...
ValueError: vertex 100 not in the graph
REFERENCES:
- [LZ2004]_
- [LM2024]_
.. SEEALSO::
- :meth:`~sage.graphs.graph.Graph.is_bicritical`
- :meth:`~sage.graphs.graph.Graph.is_matching_covered`
- :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.canonical_partition`
"""
if vertex not in self:
raise ValueError('vertex {} not in the graph'.format(vertex))

# u: The M neighbor of vertex
matching = self.get_matching()
u = next((a if b == vertex else b) for a, b, *_ in matching if vertex in [a, b])

# Goal: Find the vertices w such that G - w - vertex is matchable.
# In other words, there exists an odd length M-alternating vertex-w
# path in G, starting and ending with edges in M. Alternatively, there
# exists an even length M-alternating u-w path in the graph G - vertex
# starting with an edge not in M and ending with and edge in M.

# even: The set of all such vertex w
from sage.graphs.matching import M_alternating_even_mark
even = M_alternating_even_mark(G=self, matching=matching,
vertex=u)

B = set([vertex])
B.update(v for v in self if v not in even)

return B

@doc_index('Overwritten methods')
def has_perfect_matching(G, algorithm='Edmonds', solver=None, verbose=0,
*, integrality_tolerance=1e-3):
Expand Down

0 comments on commit 6ef076b

Please sign in to comment.