passagemath-graphs 10.5.43__cp311-cp311-macosx_14_0_arm64.whl → 10.6.1rc2__cp311-cp311-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-311-darwin.so +0 -0
  10. sage/combinat/designs/designs_pyx.pyx +2 -2
  11. sage/combinat/designs/evenly_distributed_sets.cpython-311-darwin.so +0 -0
  12. sage/combinat/designs/evenly_distributed_sets.pyx +4 -4
  13. sage/combinat/designs/gen_quadrangles_with_spread.cpython-311-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-311-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-311-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-311-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-311-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-311-darwin.so +0 -0
  39. sage/graphs/asteroidal_triples.pyx +24 -3
  40. sage/graphs/base/boost_graph.cpython-311-darwin.so +0 -0
  41. sage/graphs/base/boost_graph.pxd +3 -3
  42. sage/graphs/base/c_graph.cpython-311-darwin.so +0 -0
  43. sage/graphs/base/c_graph.pyx +1 -1
  44. sage/graphs/base/dense_graph.cpython-311-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-311-darwin.so +0 -0
  48. sage/graphs/base/sparse_graph.cpython-311-darwin.so +0 -0
  49. sage/graphs/base/static_dense_graph.cpython-311-darwin.so +0 -0
  50. sage/graphs/base/static_sparse_backend.cpython-311-darwin.so +0 -0
  51. sage/graphs/base/static_sparse_backend.pyx +8 -5
  52. sage/graphs/base/static_sparse_graph.cpython-311-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-311-darwin.so +0 -0
  56. sage/graphs/centrality.pyx +82 -9
  57. sage/graphs/cographs.py +1 -1
  58. sage/graphs/comparability.cpython-311-darwin.so +0 -0
  59. sage/graphs/comparability.pyx +64 -26
  60. sage/graphs/connectivity.cpython-311-darwin.so +0 -0
  61. sage/graphs/convexity_properties.cpython-311-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-311-darwin.so +0 -0
  66. sage/graphs/dot2tex_utils.py +1 -1
  67. sage/graphs/edge_connectivity.cpython-311-darwin.so +0 -0
  68. sage/graphs/generators/basic.py +1 -1
  69. sage/graphs/generators/distance_regular.cpython-311-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-311-darwin.so +0 -0
  76. sage/graphs/generic_graph_pyx.pyx +58 -11
  77. sage/graphs/genus.cpython-311-darwin.so +0 -0
  78. sage/graphs/genus.pyx +3 -4
  79. sage/graphs/graph.py +291 -8
  80. sage/graphs/graph_coloring.cpython-311-darwin.so +0 -0
  81. sage/graphs/graph_database.py +67 -12
  82. sage/graphs/graph_decompositions/bandwidth.cpython-311-darwin.so +0 -0
  83. sage/graphs/graph_decompositions/clique_separators.cpython-311-darwin.so +0 -0
  84. sage/graphs/graph_decompositions/clique_separators.pyx +24 -3
  85. sage/graphs/graph_decompositions/cutwidth.cpython-311-darwin.so +0 -0
  86. sage/graphs/graph_decompositions/fast_digraph.cpython-311-darwin.so +0 -0
  87. sage/graphs/graph_decompositions/fast_digraph.pyx +1 -1
  88. sage/graphs/graph_decompositions/graph_products.cpython-311-darwin.so +0 -0
  89. sage/graphs/graph_decompositions/graph_products.pyx +67 -21
  90. sage/graphs/graph_decompositions/modular_decomposition.cpython-311-darwin.so +0 -0
  91. sage/graphs/graph_decompositions/slice_decomposition.cpython-311-darwin.so +0 -0
  92. sage/graphs/graph_decompositions/slice_decomposition.pyx +34 -8
  93. sage/graphs/graph_decompositions/tree_decomposition.cpython-311-darwin.so +0 -0
  94. sage/graphs/graph_decompositions/vertex_separation.cpython-311-darwin.so +0 -0
  95. sage/graphs/graph_generators.py +45 -32
  96. sage/graphs/graph_generators_pyx.cpython-311-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-311-darwin.so +0 -0
  102. sage/graphs/hyperbolicity.pyx +2 -0
  103. sage/graphs/independent_sets.cpython-311-darwin.so +0 -0
  104. sage/graphs/isoperimetric_inequalities.cpython-311-darwin.so +0 -0
  105. sage/graphs/isoperimetric_inequalities.pyx +42 -6
  106. sage/graphs/line_graph.cpython-311-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-311-darwin.so +0 -0
  111. sage/graphs/path_enumeration.pyx +2 -2
  112. sage/graphs/spanning_tree.cpython-311-darwin.so +0 -0
  113. sage/graphs/strongly_regular_db.cpython-311-darwin.so +0 -0
  114. sage/graphs/strongly_regular_db.pyx +15 -15
  115. sage/graphs/traversals.cpython-311-darwin.so +0 -0
  116. sage/graphs/traversals.pyx +13 -12
  117. sage/graphs/trees.cpython-311-darwin.so +0 -0
  118. sage/graphs/tutte_polynomial.py +1 -1
  119. sage/graphs/views.cpython-311-darwin.so +0 -0
  120. sage/graphs/weakly_chordal.cpython-311-darwin.so +0 -0
  121. sage/graphs/weakly_chordal.pyx +50 -8
  122. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-311-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
@@ -578,13 +578,14 @@ class KnotInfoDataBase(SageObject, UniqueRepresentation):
578
578
  def _create_data_sobj(self, sobj_path=None):
579
579
  r"""
580
580
  Create ``sobj`` files containing the contents of the whole table.
581
+
581
582
  To each column there is created one file containing a list of
582
583
  strings giving the entries of the database table.
583
584
 
584
585
  The length of these lists depends on the type of the corresponding
585
586
  column. If a column is used in both tables
586
587
  (``KnotInfoColumnTypes.KnotsAndLinks``) the list of proper links
587
- is appended to the list of knots. In both other cases the lenght
588
+ is appended to the list of knots. In both other cases the length
588
589
  of the list corresponds to the number of listed knots and proper
589
590
  links respectively.
590
591
 
@@ -66,6 +66,8 @@ from cysignals.signals cimport sig_on, sig_off
66
66
  from memory_allocator cimport MemoryAllocator
67
67
 
68
68
  from sage.data_structures.bitset_base cimport *
69
+ from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph
70
+ from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend
69
71
  from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digraph, free_short_digraph
70
72
 
71
73
 
@@ -125,6 +127,17 @@ def is_asteroidal_triple_free(G, certificate=False):
125
127
  Traceback (most recent call last):
126
128
  ...
127
129
  ValueError: The first parameter must be a Graph.
130
+
131
+ The method is valid for immutable graphs::
132
+
133
+ sage: G = graphs.RandomGNP(10, .7)
134
+ sage: G._backend
135
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
136
+ sage: H = Graph(G, immutable=True)
137
+ sage: H._backend
138
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
139
+ sage: G.is_asteroidal_triple_free() == H.is_asteroidal_triple_free()
140
+ True
128
141
  """
129
142
  from sage.graphs.graph import Graph
130
143
  if not isinstance(G, Graph):
@@ -146,9 +159,16 @@ def is_asteroidal_triple_free(G, certificate=False):
146
159
  # Copying the whole graph to obtain the list of neighbors quicker than by
147
160
  # calling out_neighbors. This data structure is well documented in the
148
161
  # module sage.graphs.base.static_sparse_graph
149
- cdef list int_to_vertex = list(G)
162
+ cdef list int_to_vertex
163
+ cdef StaticSparseCGraph cg
150
164
  cdef short_digraph sd
151
- init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex)
165
+ if isinstance(G, StaticSparseBackend):
166
+ cg = <StaticSparseCGraph> G._cg
167
+ sd = <short_digraph> cg.g
168
+ int_to_vertex = cg._vertex_to_labels
169
+ else:
170
+ int_to_vertex = list(G)
171
+ init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex)
152
172
 
153
173
  cdef bitset_t seen
154
174
  bitset_init(seen, n)
@@ -169,7 +189,8 @@ def is_asteroidal_triple_free(G, certificate=False):
169
189
  finally:
170
190
  # Release memory
171
191
  bitset_free(seen)
172
- free_short_digraph(sd)
192
+ if not isinstance(G, StaticSparseBackend):
193
+ free_short_digraph(sd)
173
194
 
174
195
  # ==> We return the result
175
196
 
@@ -2,14 +2,14 @@
2
2
  # distutils: language = c++
3
3
  # distutils: extra_compile_args = -std=c++11
4
4
 
5
- #*****************************************************************************
5
+ # ***************************************************************************
6
6
  # Copyright (C) 2015 Michele Borassi michele.borassi@imtlucca.it
7
7
  #
8
8
  # Distributed under the terms of the GNU General Public License (GPL)
9
9
  # as published by the Free Software Foundation; either version 2 of
10
10
  # the License, or (at your option) any later version.
11
- # http://www.gnu.org/licenses/
12
- #*****************************************************************************
11
+ # https://www.gnu.org/licenses/
12
+ # ***************************************************************************
13
13
  from libcpp.vector cimport vector
14
14
  from libcpp.pair cimport pair
15
15
 
@@ -954,7 +954,7 @@ cdef class CGraph:
954
954
  self.check_vertex(u)
955
955
  self.check_vertex(v)
956
956
  if unlikely(self.in_degrees is NULL or self.out_degrees is NULL):
957
- raise ValueError("`self.in_degree` or `self.out_degree` not allocated")
957
+ raise ValueError("`self.in_degrees` or `self.out_degrees` not allocated")
958
958
  if self.in_degrees[v] < self.out_degrees[u]:
959
959
  size = self.in_degrees[v]
960
960
  else:
@@ -1,13 +1,13 @@
1
1
  # sage_setup: distribution = sagemath-graphs
2
- #*****************************************************************************
2
+ # ***************************************************************************
3
3
  # Copyright (C) 2008-2009 Robert L. Miller <rlmillster@gmail.com>
4
4
  #
5
5
  # This program is free software: you can redistribute it and/or modify
6
6
  # it under the terms of the GNU General Public License as published by
7
7
  # the Free Software Foundation, either version 2 of the License, or
8
8
  # (at your option) any later version.
9
- # http://www.gnu.org/licenses/
10
- #*****************************************************************************
9
+ # https://www.gnu.org/licenses/
10
+ # ***************************************************************************
11
11
 
12
12
  from sage.graphs.base.c_graph cimport CGraph, CGraphBackend
13
13
  from sage.data_structures.binary_matrix cimport binary_matrix_t
@@ -17,6 +17,8 @@ cdef class DenseGraph(CGraph):
17
17
  cdef binary_matrix_t edges
18
18
  cdef inline int _add_arc_unsafe(self, int, int) except -1
19
19
  cdef inline int _del_arc_unsafe(self, int u, int v) except -1
20
+ cpdef int out_degree(self, int u) noexcept
21
+ cpdef int in_degree(self, int u) noexcept
20
22
 
21
23
  cdef int copy_dense_graph(DenseGraph dest, DenseGraph src) except -1
22
24
 
@@ -453,6 +453,50 @@ cdef class DenseGraph(CGraph):
453
453
  i = bitset_next(self.active_vertices, i + 1)
454
454
  return -1
455
455
 
456
+ cpdef int out_degree(self, int u) noexcept:
457
+ """
458
+ Return the out-degree of ``v``
459
+
460
+ INPUT:
461
+
462
+ - ``u`` -- integer
463
+
464
+ EXAMPLES::
465
+
466
+ sage: from sage.graphs.base.dense_graph import DenseGraph
467
+ sage: G = DenseGraph(5)
468
+ sage: G.add_arc(0,1)
469
+ sage: G.add_arc(1,2)
470
+ sage: G.add_arc(1,3)
471
+ sage: G.out_degree(0)
472
+ 1
473
+ sage: G.out_degree(1)
474
+ 2
475
+ """
476
+ return self.out_degrees[u]
477
+
478
+ cpdef int in_degree(self, int v) noexcept:
479
+ """
480
+ Return the in-degree of ``v``
481
+
482
+ INPUT:
483
+
484
+ - ``v`` -- integer
485
+
486
+ EXAMPLES::
487
+
488
+ sage: from sage.graphs.base.dense_graph import DenseGraph
489
+ sage: G = DenseGraph(5)
490
+ sage: G.add_arc(0,1)
491
+ sage: G.add_arc(1,2)
492
+ sage: G.add_arc(1,3)
493
+ sage: G.in_degree(0)
494
+ 0
495
+ sage: G.in_degree(1)
496
+ 1
497
+ """
498
+ return self.in_degrees[v]
499
+
456
500
 
457
501
  cdef int copy_dense_graph(DenseGraph dest, DenseGraph src) except -1:
458
502
  r"""
@@ -36,6 +36,7 @@ Classes and methods
36
36
  -------------------
37
37
  """
38
38
 
39
+ cimport cython
39
40
  from cysignals.memory cimport check_calloc, sig_free
40
41
 
41
42
  from sage.graphs.base.static_sparse_graph cimport (init_short_digraph,
@@ -125,6 +126,8 @@ cdef class StaticSparseCGraph(CGraph):
125
126
  self.number_of_loops = <int *>check_calloc(self.g.n, sizeof(int))
126
127
  except MemoryError:
127
128
  free_short_digraph(self.g)
129
+ if self._directed:
130
+ free_short_digraph(self.g_rev)
128
131
  raise
129
132
  for i in range(self.g.n):
130
133
  for tmp in range(out_degree(self.g, i)):
@@ -140,7 +143,7 @@ cdef class StaticSparseCGraph(CGraph):
140
143
  bitset_set_first_n(self.active_vertices, self.g.n)
141
144
 
142
145
  self.num_verts = self.g.n
143
- self.num_arcs = self.g.m
146
+ self.num_arcs = self.g.m if self._directed else (2 * self.g.m)
144
147
 
145
148
  def __dealloc__(self):
146
149
  r"""
@@ -394,7 +397,7 @@ cdef class StaticSparseCGraph(CGraph):
394
397
  if u < 0 or u >= self.g.n:
395
398
  raise LookupError("the vertex does not belong to the graph")
396
399
 
397
- return self.g.neighbors[u+1] - self.g.neighbors[u]
400
+ return out_degree(self.g, u)
398
401
 
399
402
  cpdef int in_degree(self, int u) except -1:
400
403
  r"""
@@ -419,9 +422,8 @@ cdef class StaticSparseCGraph(CGraph):
419
422
  raise LookupError("the vertex does not belong to the graph")
420
423
 
421
424
  if not self._directed:
422
- return self.g.neighbors[u+1] - self.g.neighbors[u]
423
- else:
424
- return self.g_rev.neighbors[u+1] - self.g_rev.neighbors[u]
425
+ return out_degree(self.g, u)
426
+ return out_degree(self.g_rev, u)
425
427
 
426
428
 
427
429
  cdef class StaticSparseBackend(CGraphBackend):
@@ -1545,6 +1547,7 @@ cdef class StaticSparseBackend(CGraphBackend):
1545
1547
  (<StaticSparseCGraph> self._cg).del_vertex(v)
1546
1548
 
1547
1549
 
1550
+ @cython.binding(True)
1548
1551
  def _run_it_on_static_instead(f):
1549
1552
  r"""
1550
1553
  A decorator function to force the (Di)Graph functions to compute from a
@@ -281,10 +281,9 @@ cdef int init_short_digraph(short_digraph g, G, edge_labelled=False,
281
281
  g.m = G.size()
282
282
 
283
283
  cdef int isdigraph = G.is_directed()
284
- cdef uint32_t i, v_id, j
284
+ cdef uint32_t i, j
285
285
  cdef list vertices = vertex_list if vertex_list is not None else list(G)
286
286
  cdef dict v_to_id = {v: i for i, v in enumerate(vertices)}
287
- cdef list neighbor_label
288
287
  cdef list edge_labels
289
288
  # Loops are not stored twice for undirected graphs
290
289
  cdef int n_edges = g.m if isdigraph else 2*g.m - G.number_of_loops()
@@ -803,21 +802,43 @@ def tarjan_strongly_connected_components(G):
803
802
  ....: s2 = Set(map(Set,scc2))
804
803
  ....: if s1 != s2:
805
804
  ....: print("Ooch !")
805
+
806
+ Immutable digraphs::
807
+
808
+ sage: from sage.graphs.base.static_sparse_graph import tarjan_strongly_connected_components
809
+ sage: G = digraphs.RandomDirectedGNP(10, .4)
810
+ sage: G._backend
811
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
812
+ sage: H = DiGraph(G, immutable=True)
813
+ sage: H._backend
814
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
815
+ sage: tarjan_strongly_connected_components(G) == tarjan_strongly_connected_components(H)
816
+ True
806
817
  """
807
818
  from sage.graphs.digraph import DiGraph
808
819
 
809
820
  if not isinstance(G, DiGraph):
810
821
  raise ValueError("G must be a DiGraph.")
811
822
 
812
- cdef MemoryAllocator mem = MemoryAllocator()
813
- cdef list int_to_vertex = list(G)
823
+ cdef list int_to_vertex
824
+ cdef StaticSparseCGraph cg
814
825
  cdef short_digraph g
815
- init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex)
826
+ if isinstance(G, StaticSparseBackend):
827
+ cg = <StaticSparseCGraph> G._cg
828
+ g = <short_digraph> cg.g
829
+ int_to_vertex = cg._vertex_to_labels
830
+ else:
831
+ int_to_vertex = list(G)
832
+ init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex)
833
+
834
+ cdef MemoryAllocator mem = MemoryAllocator()
816
835
  cdef int * scc = <int*> mem.malloc(g.n * sizeof(int))
817
836
  sig_on()
818
837
  cdef int nscc = tarjan_strongly_connected_components_C(g, scc)
819
838
  sig_off()
820
- free_short_digraph(g)
839
+
840
+ if not isinstance(G, StaticSparseBackend):
841
+ free_short_digraph(g)
821
842
 
822
843
  cdef int i
823
844
  cdef list output = [[] for i in range(nscc)]
@@ -873,6 +894,7 @@ cdef void strongly_connected_components_digraph_C(short_digraph g, int nscc, int
873
894
 
874
895
  output.n = nscc
875
896
  output.m = m
897
+ output.edge_labels = NULL
876
898
 
877
899
  output.neighbors = <uint32_t **> check_allocarray((1+<int>output.n), sizeof(uint32_t *))
878
900
 
@@ -921,15 +943,37 @@ def strongly_connected_components_digraph(G):
921
943
  ....: for e in g.edges(sort=False):
922
944
  ....: assert(sccs[e[0]]==sccs[e[1]] or scc_digraph.has_edge(sccs[e[0]],sccs[e[1]]))
923
945
  ....: assert(sccs[e[0]] >= sccs[e[1]])
946
+
947
+ Immutable digraphs::
948
+
949
+ sage: from sage.graphs.base.static_sparse_graph import strongly_connected_components_digraph
950
+ sage: G = digraphs.RandomDirectedGNP(10, .4)
951
+ sage: G._backend
952
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
953
+ sage: H = DiGraph(G, immutable=True)
954
+ sage: H._backend
955
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
956
+ sage: A = strongly_connected_components_digraph(G)[0]
957
+ sage: B = strongly_connected_components_digraph(H)[0]
958
+ sage: A.is_isomorphic(B)
959
+ True
924
960
  """
925
961
  from sage.graphs.digraph import DiGraph
926
962
  if not isinstance(G, DiGraph):
927
963
  raise ValueError("G must be a DiGraph.")
928
964
 
929
- cdef MemoryAllocator mem = MemoryAllocator()
930
- cdef list int_to_vertex = list(G)
965
+ cdef list int_to_vertex
966
+ cdef StaticSparseCGraph cg
931
967
  cdef short_digraph g, scc_g
932
- init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex)
968
+ if isinstance(G, StaticSparseBackend):
969
+ cg = <StaticSparseCGraph> G._cg
970
+ g = <short_digraph> cg.g
971
+ int_to_vertex = cg._vertex_to_labels
972
+ else:
973
+ int_to_vertex = list(G)
974
+ init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex)
975
+
976
+ cdef MemoryAllocator mem = MemoryAllocator()
933
977
  cdef int * scc = <int*> mem.malloc(g.n * sizeof(int))
934
978
  cdef int i, j, nscc
935
979
  cdef list edges = []
@@ -945,7 +989,11 @@ def strongly_connected_components_digraph(G):
945
989
  edges.append((i, scc_g.neighbors[i][j]))
946
990
  output.add_edges(edges)
947
991
  sig_off()
948
- free_short_digraph(g)
992
+
993
+ if not isinstance(G, StaticSparseBackend):
994
+ free_short_digraph(g)
995
+ free_short_digraph(scc_g)
996
+
949
997
  return output, {v: scc[i] for i, v in enumerate(int_to_vertex)}
950
998
 
951
999
 
@@ -969,7 +1017,8 @@ cdef void free_short_digraph(short_digraph g) noexcept:
969
1017
  """
970
1018
  sig_free(g.edges)
971
1019
  sig_free(g.neighbors)
972
- cpython.Py_XDECREF(g.edge_labels)
1020
+ if g.edge_labels != NULL:
1021
+ cpython.Py_XDECREF(g.edge_labels)
973
1022
 
974
1023
 
975
1024
  def triangles_count(G):
@@ -987,14 +1036,35 @@ def triangles_count(G):
987
1036
  {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0}
988
1037
  sage: sum(triangles_count(graphs.CompleteGraph(15)).values()) == 3*binomial(15,3) # needs sage.symbolic
989
1038
  True
1039
+
1040
+ TESTS:
1041
+
1042
+ Immutable graphs::
1043
+
1044
+ sage: from sage.graphs.base.static_sparse_graph import triangles_count
1045
+ sage: G = graphs.RandomGNP(10, .7)
1046
+ sage: G._backend
1047
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
1048
+ sage: H = Graph(G, immutable=True)
1049
+ sage: H._backend
1050
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
1051
+ sage: triangles_count(G) == triangles_count(H)
1052
+ True
990
1053
  """
991
1054
  from sage.rings.integer import Integer
992
1055
  G._scream_if_not_simple()
993
1056
 
994
1057
  # g is a copy of G. If G is internally a static sparse graph, we use it.
995
- cdef list int_to_vertex = list(G)
1058
+ cdef list int_to_vertex
1059
+ cdef StaticSparseCGraph cg
996
1060
  cdef short_digraph g
997
- init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex)
1061
+ if isinstance(G, StaticSparseBackend):
1062
+ cg = <StaticSparseCGraph> G._cg
1063
+ g = <short_digraph> cg.g
1064
+ int_to_vertex = cg._vertex_to_labels
1065
+ else:
1066
+ int_to_vertex = list(G)
1067
+ init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex)
998
1068
 
999
1069
  cdef uint64_t * count = <uint64_t *> check_calloc(G.order(), sizeof(uint64_t))
1000
1070
 
@@ -1028,7 +1098,8 @@ def triangles_count(G):
1028
1098
 
1029
1099
  ans = {w: Integer(count[i] // 2) for i, w in enumerate(int_to_vertex)}
1030
1100
 
1031
- free_short_digraph(g)
1101
+ if not isinstance(G, StaticSparseBackend):
1102
+ free_short_digraph(g)
1032
1103
  sig_free(count)
1033
1104
  return ans
1034
1105
 
@@ -1112,7 +1183,7 @@ def spectral_radius(G, prec=1e-10):
1112
1183
  sage: while not G.is_strongly_connected():
1113
1184
  ....: shuffle(r)
1114
1185
  ....: G.add_edges(enumerate(r), loops=False)
1115
- sage: spectral_radius(G, 1e-10) # random
1186
+ sage: spectral_radius(G, 1e-10) # random # long time
1116
1187
  (1.9997956006500042, 1.9998043797692782)
1117
1188
 
1118
1189
  The algorithm takes care of multiple edges::
@@ -392,6 +392,17 @@ class BipartiteGraph(Graph):
392
392
  Traceback (most recent call last):
393
393
  ...
394
394
  LookupError: vertex (7) is not a vertex of the graph
395
+
396
+ Check that :issue:`39295` is fixed::
397
+
398
+ sage: # needs sage.modules
399
+ sage: B = BipartiteGraph(matrix([[1, 1], [1, 1]]), immutable=True)
400
+ sage: print(B.vertices(), B.edges())
401
+ [0, 1, 2, 3] [(0, 2, None), (0, 3, None), (1, 2, None), (1, 3, None)]
402
+ sage: B.add_vertices([4], left=True)
403
+ Traceback (most recent call last):
404
+ ...
405
+ ValueError: graph is immutable; please change a copy instead (use function copy())
395
406
  """
396
407
  if kwds is None:
397
408
  kwds = {'loops': False}
@@ -468,32 +479,31 @@ class BipartiteGraph(Graph):
468
479
  if kwds.get("multiedges", False) and kwds.get("weighted", False):
469
480
  raise TypeError("weighted multi-edge bipartite graphs from "
470
481
  "reduced adjacency matrix not supported")
471
- Graph.__init__(self, *args, **kwds)
472
482
  ncols = data.ncols()
473
483
  nrows = data.nrows()
474
484
  self.left = set(range(ncols))
475
485
  self.right = set(range(ncols, nrows + ncols))
476
486
 
477
- # ensure that the vertices exist even if there
478
- # are no associated edges (trac #10356)
479
- self.add_vertices(self.left)
480
- self.add_vertices(self.right)
481
-
482
- if kwds.get("multiedges", False):
483
- for ii in range(ncols):
484
- for jj in range(nrows):
485
- if data[jj, ii]:
486
- self.add_edges([(ii, jj + ncols)] * data[jj, ii])
487
- elif kwds.get("weighted", False):
488
- for ii in range(ncols):
489
- for jj in range(nrows):
490
- if data[jj, ii]:
491
- self.add_edge((ii, jj + ncols, data[jj, ii]))
492
- else:
493
- for ii in range(ncols):
494
- for jj in range(nrows):
495
- if data[jj, ii]:
496
- self.add_edge((ii, jj + ncols))
487
+ def edges():
488
+ if kwds.get("multiedges", False):
489
+ for ii in range(ncols):
490
+ for jj in range(nrows):
491
+ for _ in range(data[jj, ii]):
492
+ yield (ii, jj + ncols)
493
+ elif kwds.get("weighted", False):
494
+ for ii in range(ncols):
495
+ for jj in range(nrows):
496
+ if data[jj, ii]:
497
+ yield (ii, jj + ncols, data[jj, ii])
498
+ else:
499
+ for ii in range(ncols):
500
+ for jj in range(nrows):
501
+ if data[jj, ii]:
502
+ yield (ii, jj + ncols)
503
+
504
+ # ensure that construction works
505
+ # when immutable=True (issue #39295)
506
+ Graph.__init__(self, data=[range(nrows + ncols), edges()], format='vertices_and_edges', *args, **kwds)
497
507
  else:
498
508
  if partition is not None:
499
509
  left, right = set(partition[0]), set(partition[1])
@@ -2457,12 +2467,8 @@ class BipartiteGraph(Graph):
2457
2467
 
2458
2468
  B.add_edges(edges_to_keep)
2459
2469
 
2460
- attributes_to_update = ('_pos', '_assoc')
2461
- for attr in attributes_to_update:
2462
- if hasattr(self, attr) and getattr(self, attr) is not None:
2463
- d = getattr(self, attr)
2464
- value = {v: d.get(v, None) for v in B}
2465
- setattr(B, attr, value)
2470
+ B._copy_attribute_from(self, '_pos')
2471
+ B._copy_attribute_from(self, '_assoc')
2466
2472
 
2467
2473
  return B
2468
2474
 
@@ -2521,12 +2527,8 @@ class BipartiteGraph(Graph):
2521
2527
  else:
2522
2528
  # We make a copy of the graph
2523
2529
  B = BipartiteGraph(data=self.edges(sort=True), partition=[self.left, self.right])
2524
- attributes_to_update = ('_pos', '_assoc')
2525
- for attr in attributes_to_update:
2526
- if hasattr(self, attr) and getattr(self, attr) is not None:
2527
- d = getattr(self, attr)
2528
- value = {v: d.get(v, None) for v in B}
2529
- setattr(B, attr, value)
2530
+ B._copy_attribute_from(self, '_pos')
2531
+ B._copy_attribute_from(self, '_assoc')
2530
2532
  B.name("Subgraph of ({})".format(self.name()))
2531
2533
 
2532
2534
  vertices = set(vertices)
@@ -2551,7 +2553,8 @@ class BipartiteGraph(Graph):
2551
2553
  return B
2552
2554
 
2553
2555
  def canonical_label(self, partition=None, certificate=False,
2554
- edge_labels=False, algorithm=None, return_graph=True):
2556
+ edge_labels=False, algorithm=None, return_graph=True,
2557
+ immutable=None):
2555
2558
  r"""
2556
2559
  Return the canonical graph.
2557
2560
 
@@ -2592,6 +2595,10 @@ class BipartiteGraph(Graph):
2592
2595
  instead of the canonical graph. Only available when ``'bliss'``
2593
2596
  is explicitly set as algorithm.
2594
2597
 
2598
+ - ``immutable`` -- boolean (default: ``None``); whether to create a
2599
+ mutable/immutable (di)graph. ``immutable=None`` (default) means that
2600
+ the (di)graph and its canonical (di)graph will behave the same way.
2601
+
2595
2602
  EXAMPLES::
2596
2603
 
2597
2604
  sage: B = BipartiteGraph( [(0, 4), (0, 5), (0, 6), (0, 8), (1, 5),
@@ -2651,6 +2658,19 @@ class BipartiteGraph(Graph):
2651
2658
  sage: B.canonical_label()
2652
2659
  Bipartite multi-graph on 4 vertices
2653
2660
 
2661
+ Check the behavior for immutable graphs::
2662
+
2663
+ sage: G = BipartiteGraph(graphs.CycleGraph(4))
2664
+ sage: G.canonical_label().is_immutable()
2665
+ False
2666
+ sage: G.canonical_label(immutable=True).is_immutable()
2667
+ True
2668
+ sage: G = BipartiteGraph(graphs.CycleGraph(4), immutable=True)
2669
+ sage: G.canonical_label().is_immutable()
2670
+ True
2671
+ sage: G.canonical_label(immutable=False).is_immutable()
2672
+ False
2673
+
2654
2674
  .. SEEALSO::
2655
2675
 
2656
2676
  :meth:`~sage.graphs.generic_graph.GenericGraph.canonical_label()`
@@ -2660,7 +2680,8 @@ class BipartiteGraph(Graph):
2660
2680
  certificate=certificate,
2661
2681
  edge_labels=edge_labels,
2662
2682
  algorithm=algorithm,
2663
- return_graph=return_graph)
2683
+ return_graph=return_graph,
2684
+ immutable=immutable)
2664
2685
 
2665
2686
  else:
2666
2687
  from sage.groups.perm_gps.partn_ref.refinement_graphs import search_tree
@@ -2699,7 +2720,9 @@ class BipartiteGraph(Graph):
2699
2720
  a, b, c = search_tree(GC, partition, certificate=True, dig=False)
2700
2721
  cert = {v: c[G_to[v]] for v in G_to}
2701
2722
 
2702
- C = self.relabel(perm=cert, inplace=False)
2723
+ if immutable is None:
2724
+ immutable = self.is_immutable()
2725
+ C = self.relabel(perm=cert, inplace=False, immutable=immutable)
2703
2726
 
2704
2727
  C.left = {cert[v] for v in self.left}
2705
2728
  C.right = {cert[v] for v in self.right}