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.
- {passagemath_graphs-10.5.43.dist-info → passagemath_graphs-10.6.1rc2.dist-info}/METADATA +5 -6
- {passagemath_graphs-10.5.43.dist-info → passagemath_graphs-10.6.1rc2.dist-info}/RECORD +132 -130
- sage/combinat/abstract_tree.py +188 -17
- sage/combinat/cluster_algebra_quiver/interact.py +1 -2
- sage/combinat/cluster_algebra_quiver/mutation_type.py +518 -519
- sage/combinat/cluster_algebra_quiver/quiver.py +233 -205
- sage/combinat/designs/covering_design.py +2 -6
- sage/combinat/designs/database.py +11 -10
- sage/combinat/designs/designs_pyx.cpython-312-darwin.so +0 -0
- sage/combinat/designs/designs_pyx.pyx +2 -2
- sage/combinat/designs/evenly_distributed_sets.cpython-312-darwin.so +0 -0
- sage/combinat/designs/evenly_distributed_sets.pyx +4 -4
- sage/combinat/designs/gen_quadrangles_with_spread.cpython-312-darwin.so +0 -0
- sage/combinat/designs/latin_squares.py +53 -20
- sage/combinat/designs/orthogonal_arrays.py +2 -1
- sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-312-darwin.so +0 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +22 -21
- sage/combinat/designs/resolvable_bibd.py +191 -157
- sage/combinat/designs/subhypergraph_search.cpython-312-darwin.so +0 -0
- sage/combinat/designs/subhypergraph_search.pyx +4 -4
- sage/combinat/designs/twographs.py +2 -2
- sage/combinat/finite_state_machine.py +6 -6
- sage/combinat/posets/bubble_shuffle.py +247 -0
- sage/combinat/posets/d_complete.py +3 -3
- sage/combinat/posets/elements.py +3 -3
- sage/combinat/posets/hasse_cython.cpython-312-darwin.so +0 -0
- sage/combinat/posets/hasse_cython.pyx +1 -1
- sage/combinat/posets/hasse_diagram.py +16 -22
- sage/combinat/posets/hochschild_lattice.py +158 -0
- sage/combinat/posets/incidence_algebras.py +14 -16
- sage/combinat/posets/lattices.py +51 -53
- sage/combinat/posets/linear_extension_iterator.cpython-312-darwin.so +0 -0
- sage/combinat/posets/linear_extensions.py +10 -12
- sage/combinat/posets/moebius_algebra.py +4 -4
- sage/combinat/posets/poset_examples.py +70 -23
- sage/combinat/posets/posets.py +294 -103
- sage/databases/knotinfo_db.py +2 -1
- sage/graphs/asteroidal_triples.cpython-312-darwin.so +0 -0
- sage/graphs/asteroidal_triples.pyx +24 -3
- sage/graphs/base/boost_graph.cpython-312-darwin.so +0 -0
- sage/graphs/base/boost_graph.pxd +3 -3
- sage/graphs/base/c_graph.cpython-312-darwin.so +0 -0
- sage/graphs/base/c_graph.pyx +1 -1
- sage/graphs/base/dense_graph.cpython-312-darwin.so +0 -0
- sage/graphs/base/dense_graph.pxd +5 -3
- sage/graphs/base/dense_graph.pyx +44 -0
- sage/graphs/base/graph_backends.cpython-312-darwin.so +0 -0
- sage/graphs/base/sparse_graph.cpython-312-darwin.so +0 -0
- sage/graphs/base/static_dense_graph.cpython-312-darwin.so +0 -0
- sage/graphs/base/static_sparse_backend.cpython-312-darwin.so +0 -0
- sage/graphs/base/static_sparse_backend.pyx +8 -5
- sage/graphs/base/static_sparse_graph.cpython-312-darwin.so +0 -0
- sage/graphs/base/static_sparse_graph.pyx +86 -15
- sage/graphs/bipartite_graph.py +59 -36
- sage/graphs/centrality.cpython-312-darwin.so +0 -0
- sage/graphs/centrality.pyx +82 -9
- sage/graphs/cographs.py +1 -1
- sage/graphs/comparability.cpython-312-darwin.so +0 -0
- sage/graphs/comparability.pyx +64 -26
- sage/graphs/connectivity.cpython-312-darwin.so +0 -0
- sage/graphs/convexity_properties.cpython-312-darwin.so +0 -0
- sage/graphs/convexity_properties.pyx +52 -9
- sage/graphs/digraph.py +439 -95
- sage/graphs/digraph_generators.py +174 -102
- sage/graphs/distances_all_pairs.cpython-312-darwin.so +0 -0
- sage/graphs/dot2tex_utils.py +1 -1
- sage/graphs/edge_connectivity.cpython-312-darwin.so +0 -0
- sage/graphs/generators/basic.py +1 -1
- sage/graphs/generators/distance_regular.cpython-312-darwin.so +0 -0
- sage/graphs/generators/distance_regular.pyx +1 -1
- sage/graphs/generators/families.py +37 -27
- sage/graphs/generators/random.py +2 -2
- sage/graphs/generators/smallgraphs.py +3 -3
- sage/graphs/generic_graph.py +558 -86
- sage/graphs/generic_graph_pyx.cpython-312-darwin.so +0 -0
- sage/graphs/generic_graph_pyx.pyx +58 -11
- sage/graphs/genus.cpython-312-darwin.so +0 -0
- sage/graphs/genus.pyx +3 -4
- sage/graphs/graph.py +291 -8
- sage/graphs/graph_coloring.cpython-312-darwin.so +0 -0
- sage/graphs/graph_database.py +67 -12
- sage/graphs/graph_decompositions/bandwidth.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.pyx +24 -3
- sage/graphs/graph_decompositions/cutwidth.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.pyx +1 -1
- sage/graphs/graph_decompositions/graph_products.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/graph_products.pyx +67 -21
- sage/graphs/graph_decompositions/modular_decomposition.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.pyx +34 -8
- sage/graphs/graph_decompositions/tree_decomposition.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/vertex_separation.cpython-312-darwin.so +0 -0
- sage/graphs/graph_generators.py +45 -32
- sage/graphs/graph_generators_pyx.cpython-312-darwin.so +0 -0
- sage/graphs/graph_generators_pyx.pyx +15 -15
- sage/graphs/graph_latex.py +1 -1
- sage/graphs/graph_list.py +52 -9
- sage/graphs/graph_plot.py +7 -0
- sage/graphs/hyperbolicity.cpython-312-darwin.so +0 -0
- sage/graphs/hyperbolicity.pyx +2 -0
- sage/graphs/independent_sets.cpython-312-darwin.so +0 -0
- sage/graphs/isoperimetric_inequalities.cpython-312-darwin.so +0 -0
- sage/graphs/isoperimetric_inequalities.pyx +42 -6
- sage/graphs/line_graph.cpython-312-darwin.so +0 -0
- sage/graphs/line_graph.pyx +153 -37
- sage/graphs/matching_covered_graph.py +84 -60
- sage/graphs/orientations.py +3 -18
- sage/graphs/path_enumeration.cpython-312-darwin.so +0 -0
- sage/graphs/path_enumeration.pyx +2 -2
- sage/graphs/spanning_tree.cpython-312-darwin.so +0 -0
- sage/graphs/strongly_regular_db.cpython-312-darwin.so +0 -0
- sage/graphs/strongly_regular_db.pyx +15 -15
- sage/graphs/traversals.cpython-312-darwin.so +0 -0
- sage/graphs/traversals.pyx +13 -12
- sage/graphs/trees.cpython-312-darwin.so +0 -0
- sage/graphs/tutte_polynomial.py +1 -1
- sage/graphs/views.cpython-312-darwin.so +0 -0
- sage/graphs/weakly_chordal.cpython-312-darwin.so +0 -0
- sage/graphs/weakly_chordal.pyx +50 -8
- sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-312-darwin.so +0 -0
- sage/knots/free_knotinfo_monoid.py +3 -3
- sage/knots/knotinfo.py +102 -82
- sage/knots/link.py +72 -39
- sage/topology/cubical_complex.py +4 -5
- sage/topology/delta_complex.py +4 -4
- sage/topology/simplicial_complex.py +0 -1
- sage/topology/simplicial_complex_catalog.py +6 -0
- sage/topology/simplicial_complex_examples.py +4 -16
- {passagemath_graphs-10.5.43.dist-info → passagemath_graphs-10.6.1rc2.dist-info}/WHEEL +0 -0
- {passagemath_graphs-10.5.43.dist-info → passagemath_graphs-10.6.1rc2.dist-info}/top_level.txt +0 -0
Binary file
|
@@ -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
|
1390
|
+
cdef list int_to_vertex
|
1391
|
+
cdef StaticSparseCGraph cg
|
1377
1392
|
cdef short_digraph sd
|
1378
|
-
|
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
|
-
|
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
|
-
|
1546
|
+
if not isinstance(G, StaticSparseBackend):
|
1547
|
+
free_short_digraph(sd)
|
1523
1548
|
if directed:
|
1524
|
-
|
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
|
-
|
1556
|
-
|
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
|
-
|
1622
|
-
|
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("
|
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]
|
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("
|
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
|
-
|
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
|
-
|
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 = [
|
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:
|
Binary file
|
sage/graphs/graph_database.py
CHANGED
@@ -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,
|
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
|
-
|
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
|
-
|
707
|
-
|
708
|
-
|
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
|
"""
|
Binary file
|
Binary file
|