Skip to content

Commit e4ceffe

Browse files
committed
updated is_brick()
1 parent e1208c8 commit e4ceffe

File tree

1 file changed

+138
-22
lines changed

1 file changed

+138
-22
lines changed

src/sage/graphs/matching_covered_graph.py

Lines changed: 138 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2750,13 +2750,38 @@ def is_brick(self, coNP_certificate=False):
27502750
27512751
- If the input nonbipartite matching covered graph is a brick, a
27522752
boolean ``True`` is returned if ``coNP_certificate`` is set to
2753-
``False`` otherwise a tuple ``(True, None, None)`` is returned.
2753+
``False`` otherwise a 4-tuple ``(True, None, None, None)`` is
2754+
returned.
27542755
27552756
- If the input nonbipartite matching covered graph is not a brick, a
27562757
boolean ``False`` is returned if ``coNP_certificate`` is set to
2757-
``False`` otherwise a tuple of boolean ``False``, a list of
2758-
edges constituting a nontrivial tight cut and a set of vertices of
2759-
one of the shores of the nontrivial tight cut is returned.
2758+
``False``.
2759+
2760+
- If ``coNP_certificate`` is set to ``True`` and the input nonbipartite
2761+
graph is not a brick, a 4-tuple of
2762+
2763+
1. a boolean ``False``,
2764+
2765+
2. a list of list of edges each list constituting a nontrivial tight
2766+
cut collectively representing a laminar tight cut,
2767+
2768+
3. a list of set of vertices of one of the shores of those respective
2769+
nontrivial tight cuts.
2770+
2771+
i. In case of nontrivial barrier cuts, each of the shores is a
2772+
nontrivial odd component wrt a nontrivial barrier, thus the
2773+
returned list forms mutually exclusive collection of (odd)
2774+
sets.
2775+
2776+
ii. Otherwise each of the nontrivial tight cuts is a 2-separation
2777+
cut, each of the shores form a subset sequence, with the `i`th
2778+
shore being a proper subset of the `i + 1`th shore.
2779+
2780+
4. a string showing whether the nontrivial tight cuts are barrier
2781+
cuts (if the string is 'nontrivial barrier cuts'), or 2-separation
2782+
cuts (if the string is 'nontrivial 2-separation cuts')
2783+
2784+
is returned.
27602785
27612786
EXAMPLES:
27622787
@@ -2856,12 +2881,84 @@ def is_brick(self, coNP_certificate=False):
28562881
sage: K4 = graphs.CompleteGraph(4)
28572882
sage: G = MatchingCoveredGraph(K4)
28582883
sage: G.is_brick(coNP_certificate=True)
2859-
(True, None, None)
2884+
(True, None, None, None)
28602885
sage: # K(4) ⊙ K(3, 3) is nonbipartite but not a brick
28612886
sage: H = graphs.MurtyGraph(); H.delete_edge(0, 1)
28622887
sage: G = MatchingCoveredGraph(H)
28632888
sage: G.is_brick(coNP_certificate=True)
2864-
(False, [(5, 2, None), (6, 3, None), (7, 4, None)], {5, 6, 7})
2889+
(False, [[(5, 2, None), (6, 3, None), (7, 4, None)]], [{5, 6, 7}],
2890+
'nontrivial barrier cuts')
2891+
sage: H = Graph([
2892+
....: (0, 12), (0, 13), (0, 15), (1, 4), (1, 13), (1, 14),
2893+
....: (1, 19), (2, 4), (2, 13), (2, 14), (2, 17), (3, 9),
2894+
....: (3, 13), (3, 16), (3, 21), (4, 6), (4, 7), (5, 7),
2895+
....: (5, 8), (5, 12), (6, 8), (6, 11), (7, 10), (8, 9),
2896+
....: (9, 10), (10, 11), (11, 12), (14, 15), (14, 16), (15, 16),
2897+
....: (17, 18), (17, 21), (18, 19), (18, 20), (19, 20), (20, 21)
2898+
....: ])
2899+
sage: G = MatchingCoveredGraph(H)
2900+
sage: G.is_brick(coNP_certificate=True)
2901+
(False,
2902+
[[(12, 0, None), (4, 1, None), (4, 2, None), (9, 3, None)],
2903+
[(19, 1, None), (17, 2, None), (21, 3, None)],
2904+
[(15, 0, None), (14, 1, None), (14, 2, None), (16, 3, None)]],
2905+
[{4, 5, 6, 7, 8, 9, 10, 11, 12}, {17, 18, 19, 20, 21}, {14, 15, 16}],
2906+
'nontrivial barrier cuts')
2907+
sage: J = Graph([
2908+
....: (0, 1), (0, 2), (0, 3), (0, 4), (0, 5),
2909+
....: (0, 6), (0, 7), (0, 8), (0, 9), (0, 10),
2910+
....: (1, 2), (1, 11), (2, 11), (3, 4), (3, 11),
2911+
....: (4, 11), (5, 6), (5, 11), (6, 11), (7, 8),
2912+
....: (7, 11), (8, 11), (9, 10), (9, 11), (10, 11)
2913+
....: ])
2914+
sage: G = MatchingCoveredGraph(J)
2915+
sage: G.is_brick(coNP_certificate=True)
2916+
(False,
2917+
[[(0, 3, None),
2918+
(0, 4, None),
2919+
(0, 5, None),
2920+
(0, 6, None),
2921+
(0, 7, None),
2922+
(0, 8, None),
2923+
(0, 9, None),
2924+
(0, 10, None),
2925+
(1, 11, None),
2926+
(2, 11, None)],
2927+
[(0, 5, None),
2928+
(0, 6, None),
2929+
(0, 7, None),
2930+
(0, 8, None),
2931+
(0, 9, None),
2932+
(0, 10, None),
2933+
(1, 11, None),
2934+
(2, 11, None),
2935+
(3, 11, None),
2936+
(4, 11, None)],
2937+
[(0, 7, None),
2938+
(0, 8, None),
2939+
(0, 9, None),
2940+
(0, 10, None),
2941+
(1, 11, None),
2942+
(2, 11, None),
2943+
(3, 11, None),
2944+
(4, 11, None),
2945+
(5, 11, None),
2946+
(6, 11, None)],
2947+
[(0, 9, None),
2948+
(0, 10, None),
2949+
(1, 11, None),
2950+
(2, 11, None),
2951+
(3, 11, None),
2952+
(4, 11, None),
2953+
(5, 11, None),
2954+
(6, 11, None),
2955+
(7, 11, None),
2956+
(8, 11, None)]],
2957+
[{0, 1, 2},
2958+
{0, 1, 2, 3, 4},
2959+
{0, 1, 2, 3, 4, 5, 6},
2960+
{0, 1, 2, 3, 4, 5, 6, 7, 8}],
2961+
'nontrivial 2-separation cuts')
28652962
28662963
If the input matching covered graph is bipartite, a
28672964
:exc:`ValueError` is thrown::
@@ -2908,21 +3005,22 @@ def is_brick(self, coNP_certificate=False):
29083005

29093006
# Let K be a nontrivial odd component of H := G - B. Note that
29103007
# there exists at least one such K since G is nonbipartite
2911-
nontrivial_odd_component = next(
2912-
(component for component in H.connected_components(sort=True)
2913-
if len(component) % 2 and len(component) > 1), None
2914-
)
3008+
nontrivial_odd_components = [
3009+
set(component) for component in H.connected_components(sort=True)
3010+
if len(component) % 2 and len(component) > 1
3011+
]
29153012

2916-
# Find a nontrivial barrier cut
2917-
C = [(u, v, w) if u in nontrivial_odd_component else (v, u, w)
2918-
for u, v, w in self.edge_iterator()
2919-
if (u in nontrivial_odd_component) ^ (v in nontrivial_odd_component)]
3013+
# Find a laminar set of nontrivial barrier cuts
3014+
C = [[(u, v, w) if u in nontrivial_odd_component else (v, u, w)
3015+
for u, v, w in self.edge_iterator()
3016+
if (u in nontrivial_odd_component) ^ (v in nontrivial_odd_component)]
3017+
for nontrivial_odd_component in nontrivial_odd_components]
29203018

2921-
return (False, C, set(nontrivial_odd_component))
3019+
return (False, C, nontrivial_odd_components, 'nontrivial barrier cuts')
29223020

29233021
# Check if G is 3-connected
29243022
if self.is_triconnected():
2925-
return (True, None, None) if coNP_certificate else True
3023+
return (True, None, None, None) if coNP_certificate else True
29263024

29273025
# G has a 2-vertex cut
29283026
# Compute the SPQR-tree decomposition
@@ -2934,6 +3032,7 @@ def is_brick(self, coNP_certificate=False):
29343032
if u[0] == 'P':
29353033
two_vertex_cut.extend(u[1])
29363034
break
3035+
29373036
elif u[0] == 'S' and u[1].order() > 3:
29383037
s_vertex_set = set(u[1])
29393038
s_vertex_set -= {next(u[1].vertex_iterator())} | set(u[1].neighbors(next(u[1].vertex_iterator())))
@@ -2943,6 +3042,7 @@ def is_brick(self, coNP_certificate=False):
29433042
if not two_vertex_cut:
29443043
from collections import Counter
29453044
R_frequency = Counter()
3045+
29463046
for t, g in spqr_tree:
29473047
if t == 'R':
29483048
R_frequency.update(g)
@@ -2956,17 +3056,33 @@ def is_brick(self, coNP_certificate=False):
29563056
components = H.connected_components(sort=True)
29573057

29583058
# Find a nontrivial odd component
3059+
nontrivial_tight_cut_variation = None
3060+
29593061
if all(len(c) % 2 for c in components):
2960-
nontrivial_odd_component = next(c for c in components if len(c) > 1)
3062+
nontrivial_tight_cut_variation = 'nontrivial barrier cuts'
3063+
nontrivial_odd_components = [set(component) for component in components if len(component) > 1]
3064+
29613065
else:
2962-
nontrivial_odd_component = components[0] + [two_vertex_cut[0]]
3066+
nontrivial_tight_cut_variation = 'nontrivial 2-separation cuts'
3067+
nontrivial_odd_components = []
3068+
3069+
for index, component in enumerate(components):
3070+
if index == len(components) - 1:
3071+
continue
3072+
elif not index:
3073+
nontrivial_odd_components.append(set(components[0] + [two_vertex_cut[0]]))
3074+
else:
3075+
nontrivial_odd_component = nontrivial_odd_components[-1].copy()
3076+
nontrivial_odd_component.update(component)
3077+
nontrivial_odd_components.append(nontrivial_odd_component)
29633078

2964-
C = [(u, v, w) if u in nontrivial_odd_component else (v, u, w)
2965-
for u, v, w in self.edge_iterator()
2966-
if (u in nontrivial_odd_component) ^ (v in nontrivial_odd_component)]
3079+
C = [[(u, v, w) if u in nontrivial_odd_component else (v, u, w)
3080+
for u, v, w in self.edge_iterator()
3081+
if (u in nontrivial_odd_component) ^ (v in nontrivial_odd_component)]
3082+
for nontrivial_odd_component in nontrivial_odd_components]
29673083

29683084
# Edge (u, v, w) in C are formatted so that u is in a nontrivial odd component
2969-
return (False, C, set(nontrivial_odd_component)) if coNP_certificate else False
3085+
return (False, C, nontrivial_odd_components, nontrivial_tight_cut_variation) if coNP_certificate else False
29703086

29713087
@doc_index('Overwritten methods')
29723088
def loop_edges(self, labels=True):

0 commit comments

Comments
 (0)