@@ -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