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
sage/graphs/graph_list.py CHANGED
@@ -19,7 +19,7 @@ AUTHORS:
19
19
  # ****************************************************************************
20
20
 
21
21
 
22
- def from_whatever(data):
22
+ def from_whatever(data, immutable=False):
23
23
  r"""
24
24
  Return a list of Sage Graphs, given a list of whatever kind of data.
25
25
 
@@ -28,6 +28,9 @@ def from_whatever(data):
28
28
  - ``data`` -- can be a string, a list/iterable of strings, or a readable
29
29
  file-like object
30
30
 
31
+ - ``immutable`` -- boolean (default: ``False``); whether to return immutable
32
+ or mutable graphs
33
+
31
34
  EXAMPLES::
32
35
 
33
36
  sage: l = ['N@@?N@UGAGG?gGlKCMO', ':P_`cBaC_ACd`C_@BC`ABDHaEH_@BF_@CHIK_@BCEHKL_BIKM_BFGHI']
@@ -46,11 +49,20 @@ def from_whatever(data):
46
49
  sage: with open(filename) as fobj:
47
50
  ....: graphs_list.from_whatever(fobj)
48
51
  [Graph on 15 vertices, Looped multi-graph on 17 vertices]
52
+
53
+ Check the behaviour of parameter ``immutable``::
54
+
55
+ sage: with open(filename) as fobj:
56
+ ....: any(g.is_immutable() for g in graphs_list.from_whatever(fobj))
57
+ False
58
+ sage: with open(filename) as fobj:
59
+ ....: all(g.is_immutable() for g in graphs_list.from_whatever(fobj, immutable=True))
60
+ True
49
61
  """
50
- return _from_whatever(data)
62
+ return _from_whatever(data, immutable=immutable)
51
63
 
52
64
 
53
- def _from_whatever(data, fmt=None):
65
+ def _from_whatever(data, fmt=None, immutable=False):
54
66
  """
55
67
  Implementation details of :func:`from_whatever`.
56
68
 
@@ -64,11 +76,21 @@ def _from_whatever(data, fmt=None):
64
76
  indicating that the ``Graph`` constructor should determine this for
65
77
  itself
66
78
 
79
+ - ``immutable`` -- boolean (default: ``False``); whether to return immutable
80
+ or mutable graphs
81
+
67
82
  EXAMPLES::
68
83
 
69
84
  sage: l = ['N@@?N@UGAGG?gGlKCMO', ':P_`cBaC_ACd`C_@BC`ABDHaEH_@BF_@CHIK_@BCEHKL_BIKM_BFGHI']
70
85
  sage: graphs_list.from_whatever(l)
71
86
  [Graph on 15 vertices, Looped multi-graph on 17 vertices]
87
+
88
+ Check the behaviour of parameter ``immutable``::
89
+
90
+ sage: any(g.is_immutable() for g in graphs_list.from_whatever(l))
91
+ False
92
+ sage: all(g.is_immutable() for g in graphs_list.from_whatever(l, immutable=True))
93
+ True
72
94
  """
73
95
  from sage.graphs.graph import Graph
74
96
 
@@ -110,14 +132,15 @@ def _from_whatever(data, fmt=None):
110
132
  continue
111
133
 
112
134
  if '\n' in line:
113
- out.append(_from_whatever(line.splitlines(), fmt=fmt))
135
+ out.append(_from_whatever(line.splitlines(), fmt=fmt,
136
+ immutable=immutable))
114
137
  else:
115
- out.append(Graph(line, **kwargs))
138
+ out.append(Graph(line, immutable=immutable, **kwargs))
116
139
 
117
140
  return out
118
141
 
119
142
 
120
- def from_graph6(data):
143
+ def from_graph6(data, immutable=False):
121
144
  """
122
145
  Return a list of Sage Graphs, given a list of graph6 data.
123
146
 
@@ -125,16 +148,26 @@ def from_graph6(data):
125
148
 
126
149
  - ``data`` -- can be a string, a list of strings, or a file stream
127
150
 
151
+ - ``immutable`` -- boolean (default: ``False``); whether to return immutable
152
+ or mutable graphs
153
+
128
154
  EXAMPLES::
129
155
 
130
156
  sage: l = ['N@@?N@UGAGG?gGlKCMO', 'XsGGWOW?CC?C@HQKHqOjYKC_uHWGX?P?~TqIKA`OA@SAOEcEA??']
131
157
  sage: graphs_list.from_graph6(l)
132
158
  [Graph on 15 vertices, Graph on 25 vertices]
159
+
160
+ Check the behaviour of parameter ``immutable``::
161
+
162
+ sage: any(g.is_immutable() for g in graphs_list.from_graph6(l))
163
+ False
164
+ sage: all(g.is_immutable() for g in graphs_list.from_graph6(l, immutable=True))
165
+ True
133
166
  """
134
- return _from_whatever(data, fmt='graph6')
167
+ return _from_whatever(data, fmt='graph6', immutable=immutable)
135
168
 
136
169
 
137
- def from_sparse6(data):
170
+ def from_sparse6(data, immutable=False):
138
171
  """
139
172
  Return a list of Sage Graphs, given a list of sparse6 data.
140
173
 
@@ -142,6 +175,9 @@ def from_sparse6(data):
142
175
 
143
176
  - ``data`` -- can be a string, a list of strings, or a file stream
144
177
 
178
+ - ``immutable`` -- boolean (default: ``False``); whether to return immutable
179
+ or mutable graphs
180
+
145
181
  EXAMPLES::
146
182
 
147
183
  sage: g1 = ':P_`cBaC_ACd`C_@BC`ABDHaEH_@BF_@CHIK_@BCEHKL_BIKM_BFGHI'
@@ -152,8 +188,15 @@ def from_sparse6(data):
152
188
  sage: g2 += 'BA@XCs\\NggWSOJIDbHh@?A@aF'
153
189
  sage: graphs_list.from_sparse6([g1, g2])
154
190
  [Looped multi-graph on 17 vertices, Looped multi-graph on 39 vertices]
191
+
192
+ Check the behaviour of parameter ``immutable``::
193
+
194
+ sage: any(g.is_immutable() for g in graphs_list.from_sparse6([g1, g2]))
195
+ False
196
+ sage: all(g.is_immutable() for g in graphs_list.from_sparse6([g1, g2], immutable=True))
197
+ True
155
198
  """
156
- return _from_whatever(data, fmt='sparse6')
199
+ return _from_whatever(data, fmt='sparse6', immutable=immutable)
157
200
 
158
201
 
159
202
  def to_graph6(graphs, file=None, output_list=False):
sage/graphs/graph_plot.py CHANGED
@@ -161,6 +161,13 @@ layout_options = {
161
161
  'tree_orientation':
162
162
  'The direction of tree branches -- \'up\', \'down\', '
163
163
  '\'left\' or \'right\'.',
164
+ 'external_face':
165
+ 'A list of the vertices of the external face of the graph, '
166
+ 'used for Tutte embedding layout.',
167
+ 'external_face_pos':
168
+ 'A dictionary specifying the positions of the external face of the '
169
+ 'graph, used for Tutte embedding layout. If none specified, the'
170
+ 'external face is a regular polygon.',
164
171
  'save_pos':
165
172
  'Whether or not to save the computed position for the graph.',
166
173
  'dim':
@@ -1160,6 +1160,7 @@ def hyperbolicity(G,
1160
1160
 
1161
1161
  Asking for an approximation in a grid graph::
1162
1162
 
1163
+ sage: # needs sage.rings.real_mpfr
1163
1164
  sage: from sage.graphs.hyperbolicity import hyperbolicity
1164
1165
  sage: G = graphs.Grid2dGraph(2, 10)
1165
1166
  sage: L,C,U = hyperbolicity(G, algorithm='CCL', approximation_factor=1.5); L,U
@@ -1175,6 +1176,7 @@ def hyperbolicity(G,
1175
1176
 
1176
1177
  Asking for an approximation in a cycle graph::
1177
1178
 
1179
+ sage: # needs sage.rings.real_mpfr
1178
1180
  sage: from sage.graphs.hyperbolicity import hyperbolicity
1179
1181
  sage: G = graphs.CycleGraph(10)
1180
1182
  sage: L,C,U = hyperbolicity(G, algorithm='CCL', approximation_factor=1.5); L,U
@@ -24,9 +24,11 @@ Authors:
24
24
  from cysignals.signals cimport sig_on, sig_off
25
25
  from cysignals.memory cimport check_malloc, sig_free
26
26
 
27
- from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digraph, free_short_digraph
28
27
  from sage.data_structures.binary_matrix cimport *
29
28
  from sage.graphs.base.static_dense_graph cimport dense_graph_init
29
+ from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph
30
+ from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend
31
+ from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digraph, free_short_digraph
30
32
 
31
33
  from sage.rings.infinity import Infinity
32
34
  from sage.rings.rational_field import QQ
@@ -89,6 +91,17 @@ def cheeger_constant(g):
89
91
  Traceback (most recent call last):
90
92
  ...
91
93
  ValueError: Cheeger constant is not defined for the empty graph
94
+
95
+ Immutable graph::
96
+
97
+ sage: G = graphs.RandomGNP(10, .7)
98
+ sage: G._backend
99
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
100
+ sage: H = Graph(G, immutable=True)
101
+ sage: H._backend
102
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
103
+ sage: G.cheeger_constant() == H.cheeger_constant()
104
+ True
92
105
  """
93
106
  if g.is_directed():
94
107
  raise ValueError("Cheeger constant is only defined on non-oriented graph")
@@ -98,8 +111,9 @@ def cheeger_constant(g):
98
111
  elif g.num_verts() == 1:
99
112
  return Infinity
100
113
  elif not g.is_connected():
101
- return QQ((0, 1))
114
+ return QQ.zero()
102
115
 
116
+ cdef StaticSparseCGraph cg
103
117
  cdef short_digraph sd # a copy of the graph g
104
118
  cdef int * subgraph # vertices of the subgraph (stack)
105
119
  cdef int * bitsubgraph # vertices of the subgraph (bit array of +1 (in) or -1 (not in))
@@ -111,7 +125,11 @@ def cheeger_constant(g):
111
125
  cdef unsigned long vmin = 1 # value of the volume for the min
112
126
  cdef int i
113
127
 
114
- init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g))
128
+ if isinstance(g, StaticSparseBackend):
129
+ cg = <StaticSparseCGraph> g._cg
130
+ sd = <short_digraph> cg.g
131
+ else:
132
+ init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g))
115
133
 
116
134
  subgraph = <int *> check_malloc(sd.n * sizeof(int))
117
135
  bitsubgraph = <int *> check_malloc(sd.n * sizeof(int))
@@ -171,7 +189,8 @@ def cheeger_constant(g):
171
189
  return QQ((bmin, vmin))
172
190
 
173
191
  finally:
174
- free_short_digraph(sd)
192
+ if not isinstance(g, StaticSparseBackend):
193
+ free_short_digraph(sd)
175
194
  sig_free(subgraph)
176
195
  sig_free(bitsubgraph)
177
196
  sig_off()
@@ -225,6 +244,17 @@ def edge_isoperimetric_number(g):
225
244
  Traceback (most recent call last):
226
245
  ...
227
246
  ValueError: edge-isoperimetric number not defined for the empty graph
247
+
248
+ Immutable graph::
249
+
250
+ sage: G = graphs.RandomGNP(10, .7)
251
+ sage: G._backend
252
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
253
+ sage: H = Graph(G, immutable=True)
254
+ sage: H._backend
255
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
256
+ sage: G.edge_isoperimetric_number() == H.edge_isoperimetric_number()
257
+ True
228
258
  """
229
259
  if g.is_directed():
230
260
  raise ValueError("edge isoperimetric number is only defined on non-oriented graph")
@@ -236,6 +266,7 @@ def edge_isoperimetric_number(g):
236
266
  elif not g.is_connected():
237
267
  return QQ((0, 1))
238
268
 
269
+ cdef StaticSparseCGraph cg
239
270
  cdef short_digraph sd # a copy of the graph g
240
271
  cdef int * subgraph # vertices of the subgraph (stack)
241
272
  cdef int * bitsubgraph # vertices of the subgraph (bit array of +1 (in) or -1 (not in))
@@ -245,7 +276,11 @@ def edge_isoperimetric_number(g):
245
276
  cdef int u = 0 # current vertex
246
277
  cdef int i
247
278
 
248
- init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g))
279
+ if isinstance(g, StaticSparseBackend):
280
+ cg = <StaticSparseCGraph> g._cg
281
+ sd = <short_digraph> cg.g
282
+ else:
283
+ init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g))
249
284
 
250
285
  cdef unsigned long bmin = sd.neighbors[1] - sd.neighbors[0] # value of boundary for the min
251
286
  cdef unsigned long vmin = 1 # value of the volume for the min
@@ -311,7 +346,8 @@ def edge_isoperimetric_number(g):
311
346
 
312
347
  finally:
313
348
  sig_off()
314
- free_short_digraph(sd)
349
+ if not isinstance(g, StaticSparseBackend):
350
+ free_short_digraph(sd)
315
351
  sig_free(subgraph)
316
352
  sig_free(bitsubgraph)
317
353
 
@@ -128,6 +128,8 @@ Functions
128
128
  ---------
129
129
  """
130
130
 
131
+ from sage.structure.element cimport parent
132
+
131
133
 
132
134
  def is_line_graph(g, certificate=False):
133
135
  r"""
@@ -264,21 +266,36 @@ def is_line_graph(g, certificate=False):
264
266
  return True
265
267
 
266
268
 
267
- def line_graph(g, labels=True):
269
+ def line_graph(g, labels=True, return_labels=False, immutable=None):
268
270
  """
269
- Return the line graph of the (di)graph ``g``.
271
+ Return the line graph of the (di)graph ``g`` (multiedges and loops allowed).
270
272
 
271
273
  INPUT:
272
274
 
273
275
  - ``labels`` -- boolean (default: ``True``); whether edge labels should be
274
276
  taken in consideration. If ``labels=True``, the vertices of the line graph
275
- will be triples ``(u,v,label)``, and pairs of vertices otherwise.
276
-
277
- The line graph of an undirected graph G is an undirected graph H such that
278
- the vertices of H are the edges of G and two vertices e and f of H are
277
+ will be triples ``(u,v,label)``, and pairs of vertices otherwise. In case
278
+ of multiple edges, the vertices of the line graph will be triples
279
+ ``(u,v,an integer)``.
280
+
281
+ - ``return_labels`` -- boolean (default: ``False``); whether edge labels should
282
+ be stored or not. If g has multiple edges and if ``return_labels=True``, the
283
+ method returns a list the first element of which is the line-graph of g
284
+ and the second element is a dictionary {vertex of the line-graph: former
285
+ corresponding edge with its original label}. If ``return_labels=False``,
286
+ the method returns only the line-graph.
287
+
288
+ - ``immutable`` -- boolean (default: ``None``); whether to create a
289
+ mutable/immutable (di)graph. ``immutable=None`` (default) means that the
290
+ (di)graph and its line (di)graph will behave the same way.
291
+
292
+ The line graph of an undirected graph G is an undirected simple graph H such
293
+ that the vertices of H are the edges of G and two vertices e and f of H are
279
294
  adjacent if e and f share a common vertex in G. In other words, an edge in H
280
295
  represents a path of length 2 in G.
281
296
 
297
+ Loops are not adjacent to themselves.
298
+
282
299
  The line graph of a directed graph G is a directed graph H such that the
283
300
  vertices of H are the edges of G and two vertices e and f of H are adjacent
284
301
  if e and f share a common vertex in G and the terminal vertex of e is the
@@ -312,7 +329,7 @@ def line_graph(g, labels=True):
312
329
  (1, 2, None),
313
330
  (1, 3, None),
314
331
  (2, 3, None)]
315
- sage: h.am() # needs sage.modules
332
+ sage: h.am() # needs sage.modules
316
333
  [0 1 1 1 1 0]
317
334
  [1 0 1 1 0 1]
318
335
  [1 1 0 0 1 1]
@@ -322,7 +339,7 @@ def line_graph(g, labels=True):
322
339
  sage: h2 = g.line_graph(labels=False)
323
340
  sage: h2.vertices(sort=True)
324
341
  [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
325
- sage: h2.am() == h.am() # needs sage.modules
342
+ sage: h2.am() == h.am() # needs sage.modules
326
343
  True
327
344
  sage: g = DiGraph([[1..4], lambda i,j: i < j])
328
345
  sage: h = g.line_graph()
@@ -339,6 +356,40 @@ def line_graph(g, labels=True):
339
356
  ((1, 3, None), (3, 4, None), None),
340
357
  ((2, 3, None), (3, 4, None), None)]
341
358
 
359
+ Examples with multiple edges::
360
+
361
+ sage: L = Graph([(0,1),(0,1),(1,2)],multiedges=True).line_graph()
362
+ sage: L.edges()
363
+ [((0, 1, 0), (0, 1, 1), None), ((0, 1, 1), (1, 2, 2), None),
364
+ ((0, 1, 0), (1, 2, 2), None)]
365
+ sage: G = Graph([(0,1),(0,1,'a'),(0,1,'b'),(0,2),(1,2,'c')],
366
+ ....: multiedges=True)
367
+ sage: L = G.line_graph(False,True)
368
+ sage: L[0].edges()
369
+ [((0, 1, 1), (0, 1, 2), None), ((0, 1, 0), (0, 1, 2), None),
370
+ ((0, 1, 2), (0, 2, 3), None), ((0, 1, 2), (1, 2, 4), None), ((0, 1, 0),
371
+ (0, 1, 1), None), ((0, 1, 1), (0, 2, 3), None), ((0, 1, 1),
372
+ (1, 2, 4), None), ((0, 1, 0), (0, 2, 3), None), ((0, 1, 0),
373
+ (1, 2, 4), None), ((0, 2, 3), (1, 2, 4), None)]
374
+ sage: L[1]
375
+ {(0, 1, 0): (0, 1, None),
376
+ (0, 1, 1): (0, 1, 'b'),
377
+ (0, 1, 2): (0, 1, 'a'),
378
+ (0, 2, 3): (0, 2, None),
379
+ (1, 2, 4): (1, 2, 'c')}
380
+ sage: g = DiGraph([(0,1),(0,1),(1,2)],multiedges=True)
381
+ sage: g.line_graph().edges()
382
+ [((0, 1, 1), (1, 2, 2), None), ((0, 1, 0), (1, 2, 2), None)]
383
+
384
+ An example with a loop::
385
+
386
+ sage: g = Graph([(0,0),(0,1),(0,2),(1,2)],multiedges=True,loops=True)
387
+ sage: L = g.line_graph()
388
+ sage: L.edges()
389
+ [((0, 0, None), (0, 1, None), None), ((0, 0, None), (0, 2, None), None),
390
+ ((0, 1, None), (0, 2, None), None), ((0, 1, None), (1, 2, None), None),
391
+ ((0, 2, None), (1, 2, None), None)]
392
+
342
393
  TESTS:
343
394
 
344
395
  :issue:`13787`::
@@ -349,24 +400,64 @@ def line_graph(g, labels=True):
349
400
  True
350
401
  sage: C.line_graph().is_isomorphic(g.line_graph())
351
402
  True
403
+
404
+ Check the behavior of parameter ``immutable``::
405
+
406
+ sage: G = Graph([(0, 1), (1, 2)])
407
+ sage: G.line_graph().is_immutable()
408
+ False
409
+ sage: G.line_graph(immutable=True).is_immutable()
410
+ True
411
+ sage: G = Graph([(0, 1), (1, 2)], immutable=True)
412
+ sage: G.line_graph().is_immutable()
413
+ True
414
+ sage: G.line_graph(immutable=False).is_immutable()
415
+ False
416
+ sage: G = Graph([(0, 1), (0, 1), (1, 2)], multiedges=True)
417
+ sage: G.line_graph().is_immutable()
418
+ False
419
+ sage: G.line_graph(immutable=True).is_immutable()
420
+ True
421
+ sage: G = Graph([(0, 1), (0, 1), (1, 2)], multiedges=True, immutable=True)
422
+ sage: G.line_graph().is_immutable()
423
+ True
424
+ sage: G.line_graph(immutable=False).is_immutable()
425
+ False
426
+ sage: G = DiGraph([(0, 1), (1, 2)])
427
+ sage: G.line_graph().is_immutable()
428
+ False
429
+ sage: G.line_graph(immutable=True).is_immutable()
430
+ True
352
431
  """
353
432
  cdef dict conflicts = {}
354
- cdef list elist = []
433
+ cdef dict origlabels_dic = {} # stores original labels of edges in case of multiple edges
434
+
435
+ multiple = g.has_multiple_edges()
436
+
437
+ if immutable is None:
438
+ immutable = g.is_immutable()
439
+
440
+ if multiple:
441
+ # As the edges of g are the vertices of its line graph, we need to distinguish between the multiple edges of g.
442
+ # To this aim, we assign to each edge of g an integer label (between 0 and g.size() - 1) and set labels to True
443
+ # in order to keep these labels during the construction of the line graph.
444
+ labels = True
445
+ origlabels_dic = {(u, v, id): (u, v, label)
446
+ for id, (u, v, label) in enumerate(g.edge_iterator())}
447
+ g = parent(g)([g, origlabels_dic.keys()], format='vertices_and_edges', multiedges=True)
355
448
 
356
- g._scream_if_not_simple()
357
449
  if g._directed:
358
450
  from sage.graphs.digraph import DiGraph
359
- G = DiGraph()
360
- G.add_vertices(g.edge_iterator(labels=labels))
361
- for v in g:
362
- # Connect appropriate incident edges of the vertex v
363
- G.add_edges((e, f) for e in g.incoming_edge_iterator(v, labels=labels)
364
- for f in g.outgoing_edge_iterator(v, labels=labels))
451
+ # Connect appropriate incident edges of each vertex v
452
+ arcs = ((e, f) for v in g
453
+ for e in g.incoming_edge_iterator(v, labels=labels)
454
+ for f in g.outgoing_edge_iterator(v, labels=labels))
455
+ G = DiGraph([g.edge_iterator(labels=labels), arcs],
456
+ format='vertices_and_edges', immutable=immutable)
457
+ if return_labels and multiple:
458
+ return [G, origlabels_dic]
365
459
  return G
366
460
 
367
- from sage.graphs.graph import Graph
368
- G = Graph()
369
-
370
461
  # We must sort the edges' endpoints so that (1,2,None) is seen as the
371
462
  # same edge as (2,1,None).
372
463
  #
@@ -376,25 +467,27 @@ def line_graph(g, labels=True):
376
467
  # pair in the dictionary of conflicts
377
468
 
378
469
  # 1) List of vertices in the line graph
470
+ cdef list vertices = []
379
471
  for e in g.edge_iterator(labels=labels):
380
472
  if hash(e[0]) < hash(e[1]):
381
- elist.append(e)
473
+ vertices.append(e)
382
474
  elif hash(e[0]) > hash(e[1]):
383
- elist.append((e[1], e[0]) + e[2:])
475
+ vertices.append((e[1], e[0]) + e[2:])
384
476
  else:
385
477
  # Settle the conflict arbitrarily
386
478
  conflicts[e] = e
387
479
  conflicts[(e[1], e[0]) + e[2:]] = e
388
- elist.append(e)
389
-
390
- G.add_vertices(elist)
480
+ vertices.append(e)
391
481
 
392
482
  # 2) adjacencies in the line graph
483
+ cdef list edges = []
484
+ cdef list elist
485
+ from itertools import combinations
393
486
  for v in g:
394
487
  elist = []
395
488
 
396
489
  # Add the edge to the list, according to hashes, as previously
397
- for e in g.edge_iterator(v, labels=labels):
490
+ for e in g.edge_iterator(v, labels=labels): # iterates over the edges incident to v
398
491
  if hash(e[0]) < hash(e[1]):
399
492
  elist.append(e)
400
493
  elif hash(e[0]) > hash(e[1]):
@@ -403,15 +496,19 @@ def line_graph(g, labels=True):
403
496
  elist.append(conflicts[e])
404
497
 
405
498
  # All pairs of elements in elist are edges of the line graph
406
- while elist:
407
- x = elist.pop()
408
- for y in elist:
409
- G.add_edge(x, y)
499
+ # if g has multiple edges, some pairs appear more than once but as G is defined as simple,
500
+ # the corresponding edges are not added as multiedges (as it should be).
501
+ edges.extend(combinations(elist, 2))
410
502
 
503
+ from sage.graphs.graph import Graph
504
+ G = Graph([vertices, edges], format='vertices_and_edges',
505
+ immutable=immutable)
506
+ if return_labels and multiple:
507
+ return [G, origlabels_dic]
411
508
  return G
412
509
 
413
510
 
414
- def root_graph(g, verbose=False):
511
+ def root_graph(g, verbose=False, immutable=None):
415
512
  r"""
416
513
  Return the root graph corresponding to the given graph ``g``.
417
514
 
@@ -424,6 +521,10 @@ def root_graph(g, verbose=False):
424
521
  - ``verbose`` -- boolean (default: ``False``); display some information
425
522
  about what is happening inside of the algorithm
426
523
 
524
+ - ``immutable`` -- boolean (default: ``None``); whether to create a
525
+ mutable/immutable (di)graph. ``immutable=None`` (default) means that the
526
+ (di)graph and its root (di)graph will behave the same way.
527
+
427
528
  .. WARNING::
428
529
 
429
530
  This code assumes that `g` is a line graph, and is a connected,
@@ -466,6 +567,19 @@ def root_graph(g, verbose=False):
466
567
  Graph on 4 vertices
467
568
  sage: G, D = root_graph(graphs.WheelGraph(5)); G
468
569
  Diamond Graph: Graph on 4 vertices
570
+
571
+ Check the behavior of parameter ``immutable``::
572
+
573
+ sage: G = graphs.CycleGraph(4)
574
+ sage: root_graph(G)[0].is_immutable()
575
+ False
576
+ sage: root_graph(G, immutable=True)[0].is_immutable()
577
+ True
578
+ sage: G = graphs.CycleGraph(4, immutable=True)
579
+ sage: root_graph(G)[0].is_immutable()
580
+ True
581
+ sage: root_graph(G, immutable=True)[0].is_immutable()
582
+ True
469
583
  """
470
584
  from sage.graphs.digraph import DiGraph
471
585
 
@@ -478,23 +592,26 @@ def root_graph(g, verbose=False):
478
592
  # is_line_graph expects a particular error message when g is not a line graph
479
593
  not_line_graph = "this graph is not a line graph !"
480
594
 
595
+ if immutable is None:
596
+ immutable = g.is_immutable()
597
+
481
598
  # Complete Graph ?
482
599
  if g.is_clique():
483
600
  from sage.graphs.generators.basic import CompleteBipartiteGraph
484
- return (CompleteBipartiteGraph(1, g.order()),
601
+ return (CompleteBipartiteGraph(1, g.order(), immutable=immutable),
485
602
  {v: (0, 1 + i) for i, v in enumerate(g)})
486
603
 
487
604
  # Diamond Graph ?
488
605
  elif g.order() == 4 and g.size() == 5:
489
606
  from sage.graphs.graph import Graph
490
- root = Graph([(0, 1), (1, 2), (2, 0), (0, 3)])
607
+ root = Graph([(0, 1), (1, 2), (2, 0), (0, 3)], immutable=immutable)
491
608
  return (root,
492
609
  g.is_isomorphic(root.line_graph(labels=False), certificate=True)[1])
493
610
 
494
611
  # Wheel on 5 vertices ?
495
612
  elif g.order() == 5 and g.size() == 8 and min(g.degree()) == 3:
496
613
  from sage.graphs.generators.basic import DiamondGraph
497
- root = DiamondGraph()
614
+ root = DiamondGraph(immutable=immutable)
498
615
  return (root,
499
616
  g.is_isomorphic(root.line_graph(labels=False), certificate=True)[1])
500
617
 
@@ -503,7 +620,7 @@ def root_graph(g, verbose=False):
503
620
  from sage.graphs.generators.platonic_solids import OctahedralGraph
504
621
  if g.is_isomorphic(OctahedralGraph()):
505
622
  from sage.graphs.generators.basic import CompleteGraph
506
- root = CompleteGraph(4)
623
+ root = CompleteGraph(4, immutable=immutable)
507
624
  return (root,
508
625
  g.is_isomorphic(root.line_graph(labels=False), certificate=True)[1])
509
626
 
@@ -592,8 +709,6 @@ def root_graph(g, verbose=False):
592
709
 
593
710
  # We now have all our cliques. Let's build the root graph to check that it
594
711
  # all fits !
595
- from sage.graphs.graph import Graph
596
- R = Graph()
597
712
 
598
713
  # Associates an integer to each clique
599
714
  cdef dict relabel = {}
@@ -617,7 +732,8 @@ def root_graph(g, verbose=False):
617
732
  print(v, L)
618
733
 
619
734
  # We now build R
620
- R.add_edges(vertex_to_map.values())
735
+ from sage.graphs.graph import Graph
736
+ R = Graph(vertex_to_map.values(), format='list_of_edges', immutable=immutable)
621
737
 
622
738
  # If g is a line graph, then it is isomorphic to the line graph of the graph
623
739
  # R that we have constructed, so we return R (and the isomorphism).