Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented methods pertaining to the canonical partition of a matching covered graph #38892

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
189 changes: 189 additions & 0 deletions src/sage/graphs/matching_covered_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@
success = False

if kwds is None:
kwds = {'loops': False}

Check warning on line 537 in src/sage/graphs/matching_covered_graph.py

View check run for this annotation

Codecov / codecov/patch

src/sage/graphs/matching_covered_graph.py#L537

Added line #L537 was not covered by tests
else:
if 'loops' in kwds and kwds['loops']:
raise ValueError('loops are not allowed in '
Expand All @@ -549,8 +549,8 @@
Graph.__init__(self, data, *args, **kwds)
success = True

except Exception as exception:
raise exception

Check warning on line 553 in src/sage/graphs/matching_covered_graph.py

View check run for this annotation

Codecov / codecov/patch

src/sage/graphs/matching_covered_graph.py#L552-L553

Added lines #L552 - L553 were not covered by tests

elif isinstance(data, Graph):
try:
Expand All @@ -570,15 +570,15 @@
M = Graph(matching)

if any(d != 1 for d in M.degree()):
raise ValueError("the input is not a matching")

Check warning on line 573 in src/sage/graphs/matching_covered_graph.py

View check run for this annotation

Codecov / codecov/patch

src/sage/graphs/matching_covered_graph.py#L573

Added line #L573 was not covered by tests

G = Graph(self, multiedges=False)

if any(not G.has_edge(edge) for edge in M.edge_iterator()):
raise ValueError("the input is not a matching of the graph")

Check warning on line 578 in src/sage/graphs/matching_covered_graph.py

View check run for this annotation

Codecov / codecov/patch

src/sage/graphs/matching_covered_graph.py#L578

Added line #L578 was not covered by tests

if (G.order() != M.order()):
raise ValueError("the input is not a perfect matching of the graph")

Check warning on line 581 in src/sage/graphs/matching_covered_graph.py

View check run for this annotation

Codecov / codecov/patch

src/sage/graphs/matching_covered_graph.py#L581

Added line #L581 was not covered by tests

self._matching = matching
else:
Expand Down Expand Up @@ -758,12 +758,12 @@

if edges is None and edge_property is None:
if vertices is None:
G = self.copy()
G.name('Matching covered subgraph of ({})'.format(self.name()))
if immutable:
G = G.copy(immutable=True)

Check warning on line 764 in src/sage/graphs/matching_covered_graph.py

View check run for this annotation

Codecov / codecov/patch

src/sage/graphs/matching_covered_graph.py#L761-L764

Added lines #L761 - L764 were not covered by tests

return G

Check warning on line 766 in src/sage/graphs/matching_covered_graph.py

View check run for this annotation

Codecov / codecov/patch

src/sage/graphs/matching_covered_graph.py#L766

Added line #L766 was not covered by tests

else:
# Check if all existent vertices are there
Expand All @@ -774,12 +774,12 @@
break

if all_existent_vertices:
G = self.copy()
G.name('Matching covered subgraph of ({})'.format(self.name()))
if immutable:
G = G.copy(immutable=True)

Check warning on line 780 in src/sage/graphs/matching_covered_graph.py

View check run for this annotation

Codecov / codecov/patch

src/sage/graphs/matching_covered_graph.py#L777-L780

Added lines #L777 - L780 were not covered by tests

return G

Check warning on line 782 in src/sage/graphs/matching_covered_graph.py

View check run for this annotation

Codecov / codecov/patch

src/sage/graphs/matching_covered_graph.py#L782

Added line #L782 was not covered by tests

G = Graph(self, weighted=self._weighted, loops=self.allows_loops(),
multiedges=self.allows_multiple_edges())
Expand Down Expand Up @@ -1036,8 +1036,8 @@
if v is None:
try:
u, v = u
except Exception:
pass

Check warning on line 1040 in src/sage/graphs/matching_covered_graph.py

View check run for this annotation

Codecov / codecov/patch

src/sage/graphs/matching_covered_graph.py#L1039-L1040

Added lines #L1039 - L1040 were not covered by tests

if u in self and v in self:
if u == v:
Expand Down Expand Up @@ -1313,7 +1313,7 @@

# Check if G has a vertex with at most 1 neighbor
if any(len(G.neighbors(v)) <= 1 for v in G):
raise ValueError('the resulting graph after the addition of'

Check warning on line 1316 in src/sage/graphs/matching_covered_graph.py

View check run for this annotation

Codecov / codecov/patch

src/sage/graphs/matching_covered_graph.py#L1316

Added line #L1316 was not covered by tests
'the edges is not matching covered')

# If all the vertices are existent, the existing perfect matching
Expand Down Expand Up @@ -1595,7 +1595,7 @@
ValueError: odd order is not allowed for matching covered graphs
"""
if in_order:
vertex = self.vertices(sort=True)[vertex]

Check warning on line 1598 in src/sage/graphs/matching_covered_graph.py

View check run for this annotation

Codecov / codecov/patch

src/sage/graphs/matching_covered_graph.py#L1598

Added line #L1598 was not covered by tests

if vertex not in self:
raise ValueError('vertex (%s) not in the graph' % str(vertex))
Expand Down Expand Up @@ -1794,6 +1794,195 @@
"""
return self._matching

@doc_index('Barriers and canonical partition')
def maximal_barrier(self, vertex):
r"""
Return the (unique) maximal barrier containing the vertex.

We use the following theorem.
janmenjayap marked this conversation as resolved.
Show resolved Hide resolved

.. 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_factor_critical`,
:meth:`~sage.graphs.graph.Graph.is_matching_covered`,
:meth:`~sage.graphs.graph.Graph.is_bicritical`

"""
if vertex not in self:
raise ValueError('vertex {} not in the graph'.format(vertex))

G = Graph(self, multiedges=False)
M = Graph(self.get_matching())
janmenjayap marked this conversation as resolved.
Show resolved Hide resolved
B = set([vertex])

# u: The M neighbor of vertex
u = next(M.neighbor_iterator(vertex))
vertex_neighbors = []

for v in G.neighbor_iterator(vertex):
janmenjayap marked this conversation as resolved.
Show resolved Hide resolved
vertex_neighbors.append(v)

# 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=G, matching=M, vertex=u)

for v in G:
janmenjayap marked this conversation as resolved.
Show resolved Hide resolved
if v not in even:
B.add(v)

return B

@doc_index('Miscellaneous methods')
def update_matching(self, matching):
r"""
Expand Down
Loading