passagemath-graphs 10.5.43__cp312-cp312-macosx_14_0_arm64.whl → 10.6.1rc2__cp312-cp312-macosx_14_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. {passagemath_graphs-10.5.43.dist-info → passagemath_graphs-10.6.1rc2.dist-info}/METADATA +5 -6
  2. {passagemath_graphs-10.5.43.dist-info → passagemath_graphs-10.6.1rc2.dist-info}/RECORD +132 -130
  3. sage/combinat/abstract_tree.py +188 -17
  4. sage/combinat/cluster_algebra_quiver/interact.py +1 -2
  5. sage/combinat/cluster_algebra_quiver/mutation_type.py +518 -519
  6. sage/combinat/cluster_algebra_quiver/quiver.py +233 -205
  7. sage/combinat/designs/covering_design.py +2 -6
  8. sage/combinat/designs/database.py +11 -10
  9. sage/combinat/designs/designs_pyx.cpython-312-darwin.so +0 -0
  10. sage/combinat/designs/designs_pyx.pyx +2 -2
  11. sage/combinat/designs/evenly_distributed_sets.cpython-312-darwin.so +0 -0
  12. sage/combinat/designs/evenly_distributed_sets.pyx +4 -4
  13. sage/combinat/designs/gen_quadrangles_with_spread.cpython-312-darwin.so +0 -0
  14. sage/combinat/designs/latin_squares.py +53 -20
  15. sage/combinat/designs/orthogonal_arrays.py +2 -1
  16. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-312-darwin.so +0 -0
  17. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +22 -21
  18. sage/combinat/designs/resolvable_bibd.py +191 -157
  19. sage/combinat/designs/subhypergraph_search.cpython-312-darwin.so +0 -0
  20. sage/combinat/designs/subhypergraph_search.pyx +4 -4
  21. sage/combinat/designs/twographs.py +2 -2
  22. sage/combinat/finite_state_machine.py +6 -6
  23. sage/combinat/posets/bubble_shuffle.py +247 -0
  24. sage/combinat/posets/d_complete.py +3 -3
  25. sage/combinat/posets/elements.py +3 -3
  26. sage/combinat/posets/hasse_cython.cpython-312-darwin.so +0 -0
  27. sage/combinat/posets/hasse_cython.pyx +1 -1
  28. sage/combinat/posets/hasse_diagram.py +16 -22
  29. sage/combinat/posets/hochschild_lattice.py +158 -0
  30. sage/combinat/posets/incidence_algebras.py +14 -16
  31. sage/combinat/posets/lattices.py +51 -53
  32. sage/combinat/posets/linear_extension_iterator.cpython-312-darwin.so +0 -0
  33. sage/combinat/posets/linear_extensions.py +10 -12
  34. sage/combinat/posets/moebius_algebra.py +4 -4
  35. sage/combinat/posets/poset_examples.py +70 -23
  36. sage/combinat/posets/posets.py +294 -103
  37. sage/databases/knotinfo_db.py +2 -1
  38. sage/graphs/asteroidal_triples.cpython-312-darwin.so +0 -0
  39. sage/graphs/asteroidal_triples.pyx +24 -3
  40. sage/graphs/base/boost_graph.cpython-312-darwin.so +0 -0
  41. sage/graphs/base/boost_graph.pxd +3 -3
  42. sage/graphs/base/c_graph.cpython-312-darwin.so +0 -0
  43. sage/graphs/base/c_graph.pyx +1 -1
  44. sage/graphs/base/dense_graph.cpython-312-darwin.so +0 -0
  45. sage/graphs/base/dense_graph.pxd +5 -3
  46. sage/graphs/base/dense_graph.pyx +44 -0
  47. sage/graphs/base/graph_backends.cpython-312-darwin.so +0 -0
  48. sage/graphs/base/sparse_graph.cpython-312-darwin.so +0 -0
  49. sage/graphs/base/static_dense_graph.cpython-312-darwin.so +0 -0
  50. sage/graphs/base/static_sparse_backend.cpython-312-darwin.so +0 -0
  51. sage/graphs/base/static_sparse_backend.pyx +8 -5
  52. sage/graphs/base/static_sparse_graph.cpython-312-darwin.so +0 -0
  53. sage/graphs/base/static_sparse_graph.pyx +86 -15
  54. sage/graphs/bipartite_graph.py +59 -36
  55. sage/graphs/centrality.cpython-312-darwin.so +0 -0
  56. sage/graphs/centrality.pyx +82 -9
  57. sage/graphs/cographs.py +1 -1
  58. sage/graphs/comparability.cpython-312-darwin.so +0 -0
  59. sage/graphs/comparability.pyx +64 -26
  60. sage/graphs/connectivity.cpython-312-darwin.so +0 -0
  61. sage/graphs/convexity_properties.cpython-312-darwin.so +0 -0
  62. sage/graphs/convexity_properties.pyx +52 -9
  63. sage/graphs/digraph.py +439 -95
  64. sage/graphs/digraph_generators.py +174 -102
  65. sage/graphs/distances_all_pairs.cpython-312-darwin.so +0 -0
  66. sage/graphs/dot2tex_utils.py +1 -1
  67. sage/graphs/edge_connectivity.cpython-312-darwin.so +0 -0
  68. sage/graphs/generators/basic.py +1 -1
  69. sage/graphs/generators/distance_regular.cpython-312-darwin.so +0 -0
  70. sage/graphs/generators/distance_regular.pyx +1 -1
  71. sage/graphs/generators/families.py +37 -27
  72. sage/graphs/generators/random.py +2 -2
  73. sage/graphs/generators/smallgraphs.py +3 -3
  74. sage/graphs/generic_graph.py +558 -86
  75. sage/graphs/generic_graph_pyx.cpython-312-darwin.so +0 -0
  76. sage/graphs/generic_graph_pyx.pyx +58 -11
  77. sage/graphs/genus.cpython-312-darwin.so +0 -0
  78. sage/graphs/genus.pyx +3 -4
  79. sage/graphs/graph.py +291 -8
  80. sage/graphs/graph_coloring.cpython-312-darwin.so +0 -0
  81. sage/graphs/graph_database.py +67 -12
  82. sage/graphs/graph_decompositions/bandwidth.cpython-312-darwin.so +0 -0
  83. sage/graphs/graph_decompositions/clique_separators.cpython-312-darwin.so +0 -0
  84. sage/graphs/graph_decompositions/clique_separators.pyx +24 -3
  85. sage/graphs/graph_decompositions/cutwidth.cpython-312-darwin.so +0 -0
  86. sage/graphs/graph_decompositions/fast_digraph.cpython-312-darwin.so +0 -0
  87. sage/graphs/graph_decompositions/fast_digraph.pyx +1 -1
  88. sage/graphs/graph_decompositions/graph_products.cpython-312-darwin.so +0 -0
  89. sage/graphs/graph_decompositions/graph_products.pyx +67 -21
  90. sage/graphs/graph_decompositions/modular_decomposition.cpython-312-darwin.so +0 -0
  91. sage/graphs/graph_decompositions/slice_decomposition.cpython-312-darwin.so +0 -0
  92. sage/graphs/graph_decompositions/slice_decomposition.pyx +34 -8
  93. sage/graphs/graph_decompositions/tree_decomposition.cpython-312-darwin.so +0 -0
  94. sage/graphs/graph_decompositions/vertex_separation.cpython-312-darwin.so +0 -0
  95. sage/graphs/graph_generators.py +45 -32
  96. sage/graphs/graph_generators_pyx.cpython-312-darwin.so +0 -0
  97. sage/graphs/graph_generators_pyx.pyx +15 -15
  98. sage/graphs/graph_latex.py +1 -1
  99. sage/graphs/graph_list.py +52 -9
  100. sage/graphs/graph_plot.py +7 -0
  101. sage/graphs/hyperbolicity.cpython-312-darwin.so +0 -0
  102. sage/graphs/hyperbolicity.pyx +2 -0
  103. sage/graphs/independent_sets.cpython-312-darwin.so +0 -0
  104. sage/graphs/isoperimetric_inequalities.cpython-312-darwin.so +0 -0
  105. sage/graphs/isoperimetric_inequalities.pyx +42 -6
  106. sage/graphs/line_graph.cpython-312-darwin.so +0 -0
  107. sage/graphs/line_graph.pyx +153 -37
  108. sage/graphs/matching_covered_graph.py +84 -60
  109. sage/graphs/orientations.py +3 -18
  110. sage/graphs/path_enumeration.cpython-312-darwin.so +0 -0
  111. sage/graphs/path_enumeration.pyx +2 -2
  112. sage/graphs/spanning_tree.cpython-312-darwin.so +0 -0
  113. sage/graphs/strongly_regular_db.cpython-312-darwin.so +0 -0
  114. sage/graphs/strongly_regular_db.pyx +15 -15
  115. sage/graphs/traversals.cpython-312-darwin.so +0 -0
  116. sage/graphs/traversals.pyx +13 -12
  117. sage/graphs/trees.cpython-312-darwin.so +0 -0
  118. sage/graphs/tutte_polynomial.py +1 -1
  119. sage/graphs/views.cpython-312-darwin.so +0 -0
  120. sage/graphs/weakly_chordal.cpython-312-darwin.so +0 -0
  121. sage/graphs/weakly_chordal.pyx +50 -8
  122. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-312-darwin.so +0 -0
  123. sage/knots/free_knotinfo_monoid.py +3 -3
  124. sage/knots/knotinfo.py +102 -82
  125. sage/knots/link.py +72 -39
  126. sage/topology/cubical_complex.py +4 -5
  127. sage/topology/delta_complex.py +4 -4
  128. sage/topology/simplicial_complex.py +0 -1
  129. sage/topology/simplicial_complex_catalog.py +6 -0
  130. sage/topology/simplicial_complex_examples.py +4 -16
  131. {passagemath_graphs-10.5.43.dist-info → passagemath_graphs-10.6.1rc2.dist-info}/WHEEL +0 -0
  132. {passagemath_graphs-10.5.43.dist-info → passagemath_graphs-10.6.1rc2.dist-info}/top_level.txt +0 -0
@@ -33,6 +33,8 @@ from memory_allocator cimport MemoryAllocator
33
33
  from sage.cpython.string cimport char_to_str
34
34
  from sage.libs.gmp.mpz cimport *
35
35
  from sage.misc.prandom import random
36
+ from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph
37
+ from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend
36
38
  from sage.graphs.base.static_sparse_graph cimport short_digraph
37
39
  from sage.graphs.base.static_sparse_graph cimport init_short_digraph
38
40
  from sage.graphs.base.static_sparse_graph cimport init_reverse
@@ -1328,6 +1330,18 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000,
1328
1330
  sage: b, C = fh(G, find_path=False)
1329
1331
  sage: b, len(C)
1330
1332
  (True, 4)
1333
+
1334
+ Immutable graphs::
1335
+
1336
+ sage: G = graphs.PetersenGraph()
1337
+ sage: H = Graph(G, immutable=True)
1338
+ sage: fh(H)
1339
+ (False, [7, 5, 0, 1, 2, 3, 8, 6, 9, 4])
1340
+ sage: fh(H, find_path=True)
1341
+ (True, [5, 0, 1, 6, 8, 3, 2, 7, 9, 4])
1342
+ sage: G = DiGraph([(0, 1), (1, 2), (2, 3)], immutable=True)
1343
+ sage: fh(G)
1344
+ (False, [0, 1, 2, 3])
1331
1345
  """
1332
1346
  G._scream_if_not_simple()
1333
1347
 
@@ -1373,13 +1387,23 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000,
1373
1387
  memset(member, 0, n * sizeof(int))
1374
1388
 
1375
1389
  # static copy of the graph for more efficient operations
1376
- cdef list int_to_vertex = list(G)
1390
+ cdef list int_to_vertex
1391
+ cdef StaticSparseCGraph cg
1377
1392
  cdef short_digraph sd
1378
- init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex)
1393
+ if isinstance(G, StaticSparseBackend):
1394
+ cg = <StaticSparseCGraph> G._cg
1395
+ sd = <short_digraph> cg.g
1396
+ int_to_vertex = cg._vertex_to_labels
1397
+ else:
1398
+ int_to_vertex = list(G)
1399
+ init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex)
1379
1400
  cdef short_digraph rev_sd
1380
1401
  cdef bint reverse = False
1381
1402
  if directed:
1382
- init_reverse(rev_sd, sd)
1403
+ if isinstance(G, StaticSparseBackend) and cg._directed:
1404
+ rev_sd = <short_digraph> cg.g_rev
1405
+ else:
1406
+ init_reverse(rev_sd, sd)
1383
1407
 
1384
1408
  # A list to store the available vertices at each step
1385
1409
  cdef list available_vertices = []
@@ -1519,9 +1543,11 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000,
1519
1543
 
1520
1544
  if bigcount * reset_bound > max_iter:
1521
1545
  output = [int_to_vertex[longest_path[i]] for i in range(longest)]
1522
- free_short_digraph(sd)
1546
+ if not isinstance(G, StaticSparseBackend):
1547
+ free_short_digraph(sd)
1523
1548
  if directed:
1524
- free_short_digraph(rev_sd)
1549
+ if not (isinstance(G, StaticSparseBackend) and cg._directed):
1550
+ free_short_digraph(rev_sd)
1525
1551
  if longest_reversed:
1526
1552
  return (False, output[::-1])
1527
1553
  return (False, output)
@@ -1552,14 +1578,15 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000,
1552
1578
  f"{int_to_vertex[path[0]]} are not adjacent")
1553
1579
 
1554
1580
  output = [int_to_vertex[path[i]] for i in range(length)]
1555
- free_short_digraph(sd)
1556
- if directed:
1581
+ if not isinstance(G, StaticSparseBackend):
1582
+ free_short_digraph(sd)
1583
+ if directed and not (isinstance(G, StaticSparseBackend) and cg._directed):
1557
1584
  free_short_digraph(rev_sd)
1558
1585
 
1559
1586
  return (True, output)
1560
1587
 
1561
1588
 
1562
- def transitive_reduction_acyclic(G):
1589
+ def transitive_reduction_acyclic(G, immutable=None):
1563
1590
  r"""
1564
1591
  Return the transitive reduction of an acyclic digraph.
1565
1592
 
@@ -1567,12 +1594,29 @@ def transitive_reduction_acyclic(G):
1567
1594
 
1568
1595
  - ``G`` -- an acyclic digraph
1569
1596
 
1597
+ - ``immutable`` -- boolean (default: ``None``); whether to create a
1598
+ mutable/immutable transitive closure. ``immutable=None`` (default) means
1599
+ that the (di)graph and its transitive closure will behave the same way.
1600
+
1570
1601
  EXAMPLES::
1571
1602
 
1572
1603
  sage: from sage.graphs.generic_graph_pyx import transitive_reduction_acyclic
1573
1604
  sage: G = posets.BooleanLattice(4).hasse_diagram()
1574
1605
  sage: G == transitive_reduction_acyclic(G.transitive_closure())
1575
1606
  True
1607
+
1608
+ TESTS:
1609
+
1610
+ Check the behavior of parameter ``immutable``::
1611
+
1612
+ sage: G = DiGraph([(0, 1)])
1613
+ sage: transitive_reduction_acyclic(G).is_immutable()
1614
+ False
1615
+ sage: transitive_reduction_acyclic(G, immutable=True).is_immutable()
1616
+ True
1617
+ sage: G = DiGraph([(0, 1)], immutable=True)
1618
+ sage: transitive_reduction_acyclic(G).is_immutable()
1619
+ True
1576
1620
  """
1577
1621
  cdef int n = G.order()
1578
1622
  cdef dict v_to_int = {vv: i for i, vv in enumerate(G)}
@@ -1616,10 +1660,13 @@ def transitive_reduction_acyclic(G):
1616
1660
  if binary_matrix_get(closure, u, v):
1617
1661
  useful_edges.append((uu, vv))
1618
1662
 
1663
+ if immutable is None:
1664
+ immutable = G.is_immutable()
1665
+
1619
1666
  from sage.graphs.digraph import DiGraph
1620
- reduced = DiGraph()
1621
- reduced.add_edges(useful_edges)
1622
- reduced.add_vertices(linear_extension)
1667
+ reduced = DiGraph([linear_extension, useful_edges],
1668
+ format='vertices_and_edges',
1669
+ immutable=immutable)
1623
1670
 
1624
1671
  binary_matrix_free(closure)
1625
1672
 
Binary file
sage/graphs/genus.pyx CHANGED
@@ -147,7 +147,7 @@ cdef class simple_connected_genus_backtracker:
147
147
 
148
148
  for v in range(self.num_verts):
149
149
  if not G.has_vertex(v):
150
- raise ValueError("Please relabel G so vertices are 0, ..., n-1")
150
+ raise ValueError("please relabel G so vertices are 0, ..., n-1")
151
151
 
152
152
  dv = G.in_degrees[v]
153
153
  self.degree[v] = 0
@@ -207,7 +207,6 @@ cdef class simple_connected_genus_backtracker:
207
207
  Quickly store the current face_map so we can recover
208
208
  the embedding it corresponds to later.
209
209
  """
210
-
211
210
  memcpy(self.face_freeze, self.face_map, self.num_darts * sizeof(int))
212
211
 
213
212
  def get_embedding(self):
@@ -586,7 +585,7 @@ def simple_connected_graph_genus(G, set_embedding=False, check=True, minimal=Tru
586
585
 
587
586
  REFERENCES:
588
587
 
589
- [1] http://www.springerlink.com/content/0776127h0r7548v7/
588
+ [1] :doi:`10.1007/s00373-007-0729-9`
590
589
  """
591
590
  cdef int style, cutoff
592
591
  oG = G # original graph
@@ -596,7 +595,7 @@ def simple_connected_graph_genus(G, set_embedding=False, check=True, minimal=Tru
596
595
 
597
596
  if check:
598
597
  if not G.is_connected():
599
- raise ValueError("Cannot compute the genus of a disconnected graph")
598
+ raise ValueError("cannot compute the genus of a disconnected graph")
600
599
 
601
600
  if G.is_directed() or G.has_multiple_edges() or G.has_loops():
602
601
  G = G.to_simple()
sage/graphs/graph.py CHANGED
@@ -2923,6 +2923,252 @@ class Graph(GenericGraph):
2923
2923
  return False
2924
2924
  return deg_one_counter == 2 and seen_counter == order
2925
2925
 
2926
+ @doc_index("Graph properties")
2927
+ def is_chordal_bipartite(self, certificate=False):
2928
+ r"""
2929
+ Check whether the given graph is chordal bipartite.
2930
+
2931
+ A graph `G` is chordal bipartite if it is bipartite and has no induced
2932
+ cycle of length at least 6.
2933
+
2934
+ An edge `(x, y) \in E` is bisimplical if `N(x) \cup N(y)` induces a
2935
+ complete bipartite subgraph of `G`.
2936
+
2937
+ A Perfect Edge Without Vertex Elimination Ordering of a bipartite graph
2938
+ `G = (X, Y, E)` is an ordering `e_1,...,e_m` of its edge set such that
2939
+ for all `1 \leq i \leq m`, `e_i` is a bisimplical edge of
2940
+ `G_i = (X, Y, E_i)` where `E_i` consists of the edges `e_i,e_{i+1}...,e_m`.
2941
+
2942
+ A graph `G` is chordal bipartite if and only if it has a Perfect Edge
2943
+ Without Vertex Elimination Ordering. See Lemma 4 in [KKD1995]_.
2944
+
2945
+ INPUT:
2946
+
2947
+ - ``certificate`` -- boolean (default: ``False``); whether to return a
2948
+ certificate
2949
+
2950
+ OUTPUT:
2951
+
2952
+ When ``certificate`` is set to ``False`` (default) this method only
2953
+ returns ``True`` or ``False`` answers. When ``certificate`` is set to
2954
+ ``True``, the method either returns:
2955
+
2956
+ * ``(True, pewveo)`` when the graph is chordal bipartite, where ``pewveo``
2957
+ is a Perfect Edge Without Vertex Elimination Ordering of edges.
2958
+
2959
+ * ``(False, cycle)`` when the graph is not chordal bipartite, where
2960
+ ``cycle`` is an odd cycle or a chordless cycle of length at least 6.
2961
+
2962
+ ALGORITHM:
2963
+
2964
+ This algorithm is based on these facts. The first one is trivial.
2965
+
2966
+ * A reduced adjacnecy matrix
2967
+ (:meth:`~sage.graphs.bipartite_graph.BipartiteGraph.reduced_adjacency_matrix`)
2968
+ of a bipartite graph has no cycle submatrix if and only if the graph is
2969
+ chordal bipartite, where cycle submatrix is 0-1 `n \times n` matrix `n \geq 3`
2970
+ with exactly two 1's in each row and column and no proper submatrix satsify
2971
+ this property.
2972
+
2973
+ * A doubly lexical ordering
2974
+ (:meth:`~sage.matrix.matrix_mod2_dense.Matrix_mod2_dense.doubly_lexical_ordering`)
2975
+ of a 0-1 matrix is `\Gamma`-free
2976
+ (:meth:`~sage.matrix.matrix_mod2_dense.Matrix_mod2_dense.is_Gamma_free`) if and
2977
+ only if the matrix has no cycle submatrix. See Theorem 5.4 in [Lub1987]_.
2978
+
2979
+ Hence, checking a doubly lexical ordering of a reduced adjacency matrix
2980
+ of a bipartite graph is `\Gamma`-free leads to detecting the graph
2981
+ is chordal bipartite. Also, this matrix contains a certificate. Hence,
2982
+ if `G` is chordal bipartite, we find a Perfect Edge Without Vertex
2983
+ Elimination Ordering of edges by Lemma 10 in [KKD1995]_.
2984
+ Otherwise, we can find a cycle submatrix by Theorem 5.2 in [Lub1987]_.
2985
+ The time complexity of this algorithm is `O(n^3)`.
2986
+
2987
+ EXAMPLES:
2988
+
2989
+ A non-bipartite graph is not chordal bipartite::
2990
+
2991
+ sage: g = graphs.CycleGraph(5)
2992
+ sage: g.is_chordal_bipartite()
2993
+ False
2994
+ sage: _, cycle = g.is_chordal_bipartite(certificate=True)
2995
+ sage: len(cycle) % 2 == 1
2996
+ True
2997
+
2998
+ A 6-cycle graph is not chordal bipartite::
2999
+
3000
+ sage: # needs sage.modules
3001
+ sage: g = graphs.CycleGraph(6)
3002
+ sage: g.is_chordal_bipartite()
3003
+ False
3004
+ sage: _, cycle = g.is_chordal_bipartite(certificate=True)
3005
+ sage: len(cycle) == 6
3006
+ True
3007
+
3008
+ A `2 \times n` grid graph is chordal bipartite::
3009
+
3010
+ sage: # needs sage.modules
3011
+ sage: g = graphs.Grid2dGraph(2, 6)
3012
+ sage: result, pewveo = g.is_chordal_bipartite(certificate=True)
3013
+ sage: result
3014
+ True
3015
+
3016
+ Let us check the certificate given by Sage is indeed a perfect
3017
+ edge without vertex elimination ordering::
3018
+
3019
+ sage: # needs sage.modules
3020
+ sage: for e in pewveo:
3021
+ ....: a = g.subgraph(vertices=g.neighbors(e[0]) + g.neighbors(e[1]))
3022
+ ....: b = BipartiteGraph(a).complement_bipartite()
3023
+ ....: if b.edges():
3024
+ ....: raise ValueError("this should never happen")
3025
+ ....: g.delete_edge(e)
3026
+
3027
+ Let us check the certificate given by Sage is indeed a
3028
+ chordless cycle of length at least 6::
3029
+
3030
+ sage: # needs sage.modules
3031
+ sage: g = graphs.Grid2dGraph(3, 6)
3032
+ sage: result, cycle = g.is_chordal_bipartite(certificate=True)
3033
+ sage: result
3034
+ False
3035
+ sage: l = len(cycle); l >= 6
3036
+ True
3037
+ sage: for i in range(len(cycle)):
3038
+ ....: if not g.has_edge(cycle[i], cycle[(i+1)%l]):
3039
+ ....: raise ValueError("this should never happen")
3040
+ sage: h = g.subgraph(vertices=cycle)
3041
+ sage: h.is_cycle()
3042
+ True
3043
+
3044
+ TESTS:
3045
+
3046
+ The algorithm works correctly for disconnected graphs::
3047
+
3048
+ sage: # needs sage.modules
3049
+ sage: c4 = graphs.CycleGraph(4)
3050
+ sage: g = c4.disjoint_union(graphs.CycleGraph(6))
3051
+ sage: g.is_chordal_bipartite()
3052
+ False
3053
+ sage: _, cycle = g.is_chordal_bipartite(certificate=True)
3054
+ sage: len(cycle) == 6
3055
+ True
3056
+ sage: g = c4.disjoint_union(graphs.Grid2dGraph(2, 6))
3057
+ sage: g.is_chordal_bipartite()
3058
+ True
3059
+ sage: _, pewveo = g.is_chordal_bipartite(certificate=True)
3060
+ sage: for e in pewveo:
3061
+ ....: a = g.subgraph(vertices=g.neighbors(e[0]) + g.neighbors(e[1]))
3062
+ ....: b = BipartiteGraph(a).complement_bipartite()
3063
+ ....: if b.edges():
3064
+ ....: raise ValueError("this should never happen")
3065
+ ....: g.delete_edge(e)
3066
+ """
3067
+ self._scream_if_not_simple()
3068
+ is_bipartite, bipartite_certificate = self.is_bipartite(certificate=True)
3069
+ if not is_bipartite:
3070
+ return False if not certificate else (False, bipartite_certificate)
3071
+
3072
+ # If the graph is not connected, we are computing the result on each
3073
+ # component
3074
+ if not self.is_connected():
3075
+ # If the user wants a certificate, we had no choice but to collect
3076
+ # the Perfect Edge Without Vertex Elimination Ordering. But we
3077
+ # return a cycle certificate immediately if we find any.
3078
+ if certificate:
3079
+ pewveo = []
3080
+ for gg in self.connected_components_subgraphs():
3081
+ b, certif = gg.is_chordal_bipartite(certificate=True)
3082
+ if not b:
3083
+ return False, certif
3084
+ pewveo.extend(certif)
3085
+ return True, pewveo
3086
+ return all(gg.is_chordal_bipartite() for gg in
3087
+ self.connected_components_subgraphs())
3088
+
3089
+ left = [v for v, c in bipartite_certificate.items() if c == 0]
3090
+ right = [v for v, c in bipartite_certificate.items() if c == 1]
3091
+ order_left = len(left)
3092
+ order_right = len(right)
3093
+
3094
+ # We set |left| > |right| for optimization, i.e. time complexity of
3095
+ # doubly lexical ordering algorithm is O(nm^2) for a n x m matrix.
3096
+ if order_left < order_right:
3097
+ left, right = right, left
3098
+ order_left, order_right = order_right, order_left
3099
+
3100
+ # create a reduced_adjacency_matrix
3101
+ from sage.rings.finite_rings.finite_field_prime_modn import FiniteField_prime_modn
3102
+ A = self.adjacency_matrix(vertices=left+right, base_ring=FiniteField_prime_modn(2))
3103
+ B = A[range(order_left), range(order_left, order_left + order_right)]
3104
+
3105
+ # get doubly lexical ordering of reduced_adjacency_matrix
3106
+ row_ordering, col_ordering = B.doubly_lexical_ordering(inplace=True)
3107
+
3108
+ # determine if B is Gamma-free or not
3109
+ is_Gamma_free, Gamma_submatrix_indices = B.is_Gamma_free(certificate=True)
3110
+
3111
+ if not certificate:
3112
+ return is_Gamma_free
3113
+
3114
+ row_ordering_dict = {k-1: v-1 for k, v in row_ordering.dict().items()}
3115
+ col_ordering_dict = {k-1: v-1 for k, v in col_ordering.dict().items()}
3116
+ row_vertices = [left[row_ordering_dict[i]] for i in range(order_left)]
3117
+ col_vertices = [right[col_ordering_dict[i]] for i in range(order_right)]
3118
+
3119
+ if is_Gamma_free:
3120
+ pewveo = dict()
3121
+ order = 0
3122
+ for i, vi in enumerate(row_vertices):
3123
+ for j, vj in enumerate(col_vertices):
3124
+ if B[i, j] == 1:
3125
+ pewveo[vi, vj] = order
3126
+ pewveo[vj, vi] = order
3127
+ order += 1
3128
+ edges = self.edges(sort=True, key=lambda x: pewveo[x[0], x[1]])
3129
+ return True, edges
3130
+
3131
+ # Find a chordless cycle of length at least 6
3132
+ r1, c1, r2, c2 = Gamma_submatrix_indices
3133
+ row_indices = [r1, r2]
3134
+ col_indices = [c1, c2]
3135
+ while not B[row_indices[-1], col_indices[-1]]:
3136
+ # find the rightmost column with different value
3137
+ # in row_indices[-2] and row_indices[-1]
3138
+ col = order_right - 1
3139
+ while col > col_indices[-1]:
3140
+ if not B[row_indices[-2], col] and B[row_indices[-1], col]:
3141
+ break
3142
+ col -= 1
3143
+ assert col > col_indices[-1]
3144
+
3145
+ # find the bottommost row with different value
3146
+ # in col_indices[-2] and col_indices[-1]
3147
+ row = order_left - 1
3148
+ while row > row_indices[-1]:
3149
+ if not B[row, col_indices[-2]] and B[row, col_indices[-1]]:
3150
+ break
3151
+ row -= 1
3152
+ assert row > row_indices[-1]
3153
+
3154
+ col_indices.append(col)
3155
+ row_indices.append(row)
3156
+
3157
+ l = len(row_indices)
3158
+ cycle = []
3159
+ for i in range(l):
3160
+ if i % 2 == 0:
3161
+ cycle.append(row_vertices[row_indices[i]])
3162
+ else:
3163
+ cycle.append(col_vertices[col_indices[i]])
3164
+ for i in reversed(range(l)):
3165
+ if i % 2 == 0:
3166
+ cycle.append(col_vertices[col_indices[i]])
3167
+ else:
3168
+ cycle.append(row_vertices[row_indices[i]])
3169
+
3170
+ return False, cycle
3171
+
2926
3172
  @doc_index("Connectivity, orientations, trees")
2927
3173
  def degree_constrained_subgraph(self, bounds, solver=None, verbose=0,
2928
3174
  *, integrality_tolerance=1e-3):
@@ -4786,6 +5032,9 @@ class Graph(GenericGraph):
4786
5032
  The diameter is defined to be the maximum distance between two vertices.
4787
5033
  It is infinite if the graph is not connected.
4788
5034
 
5035
+ The default algorithm is ``'iFUB'`` [CGILM2010]_ for unweighted graphs
5036
+ and ``'DHV'`` [Dragan2018]_ for weighted graphs.
5037
+
4789
5038
  For more information and examples on how to use input variables, see
4790
5039
  :meth:`~GenericGraph.shortest_paths` and
4791
5040
  :meth:`~Graph.eccentricity`
@@ -4876,6 +5125,12 @@ class Graph(GenericGraph):
4876
5125
  Traceback (most recent call last):
4877
5126
  ...
4878
5127
  ValueError: algorithm 'iFUB' does not work on weighted graphs
5128
+
5129
+ Check that :issue:`40013` is fixed::
5130
+
5131
+ sage: G = graphs.PetersenGraph()
5132
+ sage: G.diameter(by_weight=True)
5133
+ 2.0
4879
5134
  """
4880
5135
  if not self.order():
4881
5136
  raise ValueError("diameter is not defined for the empty graph")
@@ -4888,10 +5143,7 @@ class Graph(GenericGraph):
4888
5143
  weight_function = None
4889
5144
 
4890
5145
  if algorithm is None:
4891
- if by_weight:
4892
- algorithm = 'iFUB'
4893
- else:
4894
- algorithm = 'DHV'
5146
+ algorithm = 'DHV' if by_weight else 'iFUB'
4895
5147
  elif algorithm == 'BFS':
4896
5148
  algorithm = 'standard'
4897
5149
 
@@ -5545,7 +5797,7 @@ class Graph(GenericGraph):
5545
5797
  """
5546
5798
  from sage.graphs.print_graphs import print_graph_eps
5547
5799
  pos = self.layout(**options)
5548
- [xmin, xmax, ymin, ymax] = self._layout_bounding_box(pos)
5800
+ xmin, xmax, ymin, ymax = self._layout_bounding_box(pos)
5549
5801
  for v in pos:
5550
5802
  pos[v] = (1.8*(pos[v][0] - xmin)/(xmax - xmin) - 0.9, 1.8*(pos[v][1] - ymin)/(ymax - ymin) - 0.9)
5551
5803
  if filename[-4:] != '.eps':
@@ -7859,6 +8111,26 @@ class Graph(GenericGraph):
7859
8111
  sage: g.edge_connectivity() == min(t.edge_labels()) or not g.is_connected()
7860
8112
  True
7861
8113
 
8114
+ If the graph has an edge whose removal increases the number of connected
8115
+ components, the flow between the parts separated by this edge is one::
8116
+
8117
+ sage: g = Graph()
8118
+ sage: g.add_clique([1, 2, 3, 4])
8119
+ sage: g.add_clique([5, 6, 7, 8])
8120
+ sage: g.add_edge(1, 5)
8121
+ sage: t = g.gomory_hu_tree()
8122
+ sage: t.edge_label(1, 5)
8123
+ 1
8124
+ sage: g.flow(randint(1, 4), randint(5, 8)) == t.edge_label(1, 5)
8125
+ True
8126
+ sage: for u, v in g.edge_iterator(labels=False):
8127
+ ....: g.set_edge_label(u, v, 3)
8128
+ sage: t = g.gomory_hu_tree()
8129
+ sage: t.edge_label(1, 5)
8130
+ 3
8131
+ sage: g.flow(randint(1, 4), randint(5, 8)) == t.edge_label(1, 5)
8132
+ True
8133
+
7862
8134
  TESTS:
7863
8135
 
7864
8136
  :issue:`16475`::
@@ -7888,11 +8160,22 @@ class Graph(GenericGraph):
7888
8160
 
7889
8161
  # We use a stack to avoid recursion. An element of the stack contains
7890
8162
  # the graph to be processed and the corresponding set of "real" vertices
7891
- # (as opposed to the fakes one introduced during the computations.
7892
- if self.is_connected():
8163
+ # (as opposed to the fakes one introduced during the computations).
8164
+ blocks = self.blocks_and_cut_vertices()[0]
8165
+ if len(blocks) == 1 and len(blocks[0]) == self.order():
8166
+ # The graph is biconnected
7893
8167
  stack = [(self, frozenset(self))]
7894
8168
  else:
7895
- stack = [(cc, frozenset(cc)) for cc in self.connected_components_subgraphs()]
8169
+ stack = []
8170
+ for block in blocks:
8171
+ if len(block) == 2:
8172
+ # This block is a cut-edge. It corresponds to an edge of the
8173
+ # tree with capacity 1 if the label is None
8174
+ u, v = block
8175
+ label = self.edge_label(u, v)
8176
+ T.add_edge(u, v, 1 if label is None else label)
8177
+ else:
8178
+ stack.append((self.subgraph(block), frozenset(block)))
7896
8179
 
7897
8180
  # We now iteratively decompose the graph to build the tree
7898
8181
  while stack:
@@ -45,10 +45,9 @@ REFERENCES:
45
45
  # Distributed under the terms of the GNU General Public License (GPL)
46
46
  # https://www.gnu.org/licenses/
47
47
  # ##############################################################################
48
+ import re
48
49
 
49
50
  from . import graph
50
- import os
51
- import re
52
51
  from sage.rings.integer import Integer
53
52
  from sage.databases.sql_db import SQLDatabase, SQLQuery
54
53
  from sage.features.databases import DatabaseGraphs
@@ -323,8 +322,9 @@ class GenericGraphQuery(SQLQuery):
323
322
 
324
323
  class GraphQuery(GenericGraphQuery):
325
324
 
326
- def __init__(self, graph_db=None, query_dict=None, display_cols=None, **kwds):
327
- """
325
+ def __init__(self, graph_db=None, query_dict=None, display_cols=None,
326
+ immutable=False, **kwds):
327
+ r"""
328
328
  A query for an instance of :class:`~GraphDatabase`.
329
329
 
330
330
  This class nicely wraps the :class:`sage.databases.sql_db.SQLQuery`
@@ -355,6 +355,9 @@ class GraphQuery(GenericGraphQuery):
355
355
  column names (strings) to display in the result when running or
356
356
  showing a query
357
357
 
358
+ - ``immutable`` -- boolean (default: ``False``); whether to return
359
+ immutable or mutable graphs
360
+
358
361
  - ``kwds`` -- the columns of the database are all keywords. For a
359
362
  database table/column structure dictionary, call
360
363
  :func:`~graph_db_info`. Keywords accept both single values and lists
@@ -416,7 +419,17 @@ class GraphQuery(GenericGraphQuery):
416
419
  F_?@w 7 [1, 1, 1, 1, 1, 1, 4]
417
420
  F_?Hg 7 [1, 1, 1, 1, 1, 2, 3]
418
421
  F_?XO 7 [1, 1, 1, 1, 2, 2, 2]
422
+
423
+ Check the behavior of parameter ``immutable``::
424
+
425
+ sage: Q = GraphQuery(display_cols=['graph6'], num_vertices=3)
426
+ sage: any(g.is_immutable() for g in Q)
427
+ False
428
+ sage: Q = GraphQuery(display_cols=['graph6'], num_vertices=3, immutable=True)
429
+ sage: all(g.is_immutable() for g in Q)
430
+ True
419
431
  """
432
+ self._immutable = immutable
420
433
  if graph_db is None:
421
434
  graph_db = GraphDatabase()
422
435
  if query_dict is not None:
@@ -537,10 +550,16 @@ class GraphQuery(GenericGraphQuery):
537
550
  self.__query_string__)
538
551
  self.__query_string__ += ' ORDER BY graph_data.graph6'
539
552
 
540
- def query_iterator(self):
553
+ def query_iterator(self, immutable=None):
541
554
  """
542
555
  Return an iterator over the results list of the :class:`~GraphQuery`.
543
556
 
557
+ INPUT:
558
+
559
+ - ``immutable`` -- boolean (default: ``None``); whether to create
560
+ mutable/immutable graphs. By default (``immutable=None``), follow the
561
+ behavior of ``self``.
562
+
544
563
  EXAMPLES::
545
564
 
546
565
  sage: Q = GraphQuery(display_cols=['graph6'], num_vertices=7, diameter=5)
@@ -569,8 +588,27 @@ class GraphQuery(GenericGraphQuery):
569
588
  FEOhW
570
589
  FGC{o
571
590
  FIAHo
591
+
592
+ Check the behavior of parameter ``immutable``::
593
+
594
+ sage: Q = GraphQuery(display_cols=['graph6'], num_vertices=3)
595
+ sage: any(g.is_immutable() for g in Q.query_iterator())
596
+ False
597
+ sage: all(g.is_immutable() for g in Q.query_iterator(immutable=True))
598
+ True
599
+ sage: Q = GraphQuery(display_cols=['graph6'], num_vertices=3, immutable=True)
600
+ sage: all(g.is_immutable() for g in Q.query_iterator())
601
+ True
602
+ sage: any(g.is_immutable() for g in Q.query_iterator(immutable=False))
603
+ False
572
604
  """
573
- return iter(self.get_graphs_list())
605
+ if immutable is None:
606
+ immutable = self._immutable
607
+ s = self.__query_string__
608
+ re.sub('SELECT.*FROM ', 'SELECT graph6 FROM ', s)
609
+ q = GenericGraphQuery(s, self.__database__, self.__param_tuple__)
610
+ for g in q.query_results():
611
+ yield Graph(str(g[0]), immutable=immutable)
574
612
 
575
613
  __iter__ = query_iterator
576
614
 
@@ -690,10 +728,16 @@ class GraphQuery(GenericGraphQuery):
690
728
  format_cols=format_cols, relabel_cols=relabel,
691
729
  id_col='graph6')
692
730
 
693
- def get_graphs_list(self):
731
+ def get_graphs_list(self, immutable=None):
694
732
  """
695
733
  Return a list of Sage Graph objects that satisfy the query.
696
734
 
735
+ INPUT:
736
+
737
+ - ``immutable`` -- boolean (default: ``None``); whether to create
738
+ mutable/immutable graphs. By default (``immutable=None``), follow the
739
+ behavior of ``self``.
740
+
697
741
  EXAMPLES::
698
742
 
699
743
  sage: Q = GraphQuery(display_cols=['graph6', 'num_vertices', 'degree_sequence'], num_edges=['<=', 5], min_degree=1)
@@ -702,12 +746,23 @@ class GraphQuery(GenericGraphQuery):
702
746
  Graph on 2 vertices
703
747
  sage: len(L)
704
748
  35
749
+
750
+ Check the behavior of parameter ``immutable``::
751
+
752
+ sage: Q = GraphQuery(display_cols=['graph6'], num_vertices=3)
753
+ sage: any(g.is_immutable() for g in Q.get_graphs_list())
754
+ False
755
+ sage: all(g.is_immutable() for g in Q.get_graphs_list(immutable=True))
756
+ True
757
+ sage: Q = GraphQuery(display_cols=['graph6'], num_vertices=3, immutable=True)
758
+ sage: all(g.is_immutable() for g in Q.get_graphs_list())
759
+ True
760
+ sage: any(g.is_immutable() for g in Q.get_graphs_list(immutable=False))
761
+ False
705
762
  """
706
- s = self.__query_string__
707
- re.sub('SELECT.*FROM ', 'SELECT graph6 FROM ', s)
708
- q = GenericGraphQuery(s, self.__database__, self.__param_tuple__)
709
- graph6_list = q.query_results()
710
- return [Graph(str(g[0])) for g in graph6_list]
763
+ if immutable is None:
764
+ immutable = self._immutable
765
+ return list(self.query_iterator(immutable=immutable))
711
766
 
712
767
  def number_of(self):
713
768
  """