passagemath-graphs 10.5.10__cp39-cp39-macosx_14_0_arm64.whl → 10.5.43__cp39-cp39-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 (140) hide show
  1. {passagemath_graphs-10.5.10.dist-info → passagemath_graphs-10.5.43.dist-info}/METADATA +126 -30
  2. passagemath_graphs-10.5.43.dist-info/RECORD +256 -0
  3. {passagemath_graphs-10.5.10.dist-info → passagemath_graphs-10.5.43.dist-info}/WHEEL +2 -1
  4. passagemath_graphs.dylibs/libgmp.10.dylib +0 -0
  5. sage/all__sagemath_graphs.py +5 -0
  6. sage/combinat/abstract_tree.py +1 -1
  7. sage/combinat/binary_tree.py +1 -1
  8. sage/combinat/cluster_algebra_quiver/all.py +1 -1
  9. sage/combinat/cluster_algebra_quiver/cluster_seed.py +28 -24
  10. sage/combinat/cluster_algebra_quiver/interact.py +4 -0
  11. sage/combinat/designs/MOLS_handbook_data.py +5 -5
  12. sage/combinat/designs/bibd.py +10 -9
  13. sage/combinat/designs/covering_array.py +3 -3
  14. sage/combinat/designs/covering_design.py +2 -1
  15. sage/combinat/designs/database.py +11 -10
  16. sage/combinat/designs/designs_pyx.cpython-39-darwin.so +0 -0
  17. sage/combinat/designs/designs_pyx.pyx +13 -45
  18. sage/combinat/designs/difference_family.py +6 -6
  19. sage/combinat/designs/difference_matrices.py +1 -1
  20. sage/combinat/designs/evenly_distributed_sets.cpython-39-darwin.so +0 -0
  21. sage/combinat/designs/evenly_distributed_sets.pyx +15 -22
  22. sage/combinat/designs/ext_rep.py +9 -14
  23. sage/combinat/designs/gen_quadrangles_with_spread.cpython-39-darwin.so +0 -0
  24. sage/combinat/designs/gen_quadrangles_with_spread.pyx +1 -1
  25. sage/combinat/designs/group_divisible_designs.py +1 -1
  26. sage/combinat/designs/incidence_structures.py +8 -8
  27. sage/combinat/designs/latin_squares.py +1 -1
  28. sage/combinat/designs/orthogonal_arrays_build_recursive.py +8 -7
  29. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-39-darwin.so +0 -0
  30. sage/combinat/designs/resolvable_bibd.py +1 -1
  31. sage/combinat/designs/steiner_quadruple_systems.py +1 -1
  32. sage/combinat/designs/subhypergraph_search.cpython-39-darwin.so +0 -0
  33. sage/combinat/designs/subhypergraph_search.pyx +9 -9
  34. sage/combinat/finite_state_machine_generators.py +2 -2
  35. sage/combinat/graph_path.py +3 -3
  36. sage/combinat/interval_posets.py +10 -10
  37. sage/combinat/ordered_tree.py +1 -1
  38. sage/combinat/posets/cartesian_product.py +1 -1
  39. sage/combinat/posets/d_complete.py +1 -1
  40. sage/combinat/posets/forest.py +1 -1
  41. sage/combinat/posets/hasse_cython.cpython-39-darwin.so +0 -0
  42. sage/combinat/posets/hasse_diagram.py +8 -6
  43. sage/combinat/posets/incidence_algebras.py +8 -8
  44. sage/combinat/posets/lattices.py +28 -4
  45. sage/combinat/posets/linear_extension_iterator.cpython-39-darwin.so +0 -0
  46. sage/combinat/posets/linear_extension_iterator.pyx +2 -0
  47. sage/combinat/posets/linear_extensions.py +7 -16
  48. sage/combinat/posets/moebius_algebra.py +1 -1
  49. sage/combinat/posets/poset_examples.py +1 -1
  50. sage/combinat/posets/posets.py +54 -56
  51. sage/combinat/rooted_tree.py +3 -3
  52. sage/combinat/tamari_lattices.py +1 -1
  53. sage/ext_data/kenzo/CP2.txt +45 -0
  54. sage/ext_data/kenzo/CP3.txt +349 -0
  55. sage/ext_data/kenzo/CP4.txt +4774 -0
  56. sage/ext_data/kenzo/README.txt +49 -0
  57. sage/ext_data/kenzo/S4.txt +20 -0
  58. sage/graphs/asteroidal_triples.cpython-39-darwin.so +0 -0
  59. sage/graphs/base/boost_graph.cpython-39-darwin.so +0 -0
  60. sage/graphs/base/boost_graph.pxd +1 -1
  61. sage/graphs/base/boost_graph.pyx +1 -1
  62. sage/graphs/base/c_graph.cpython-39-darwin.so +0 -0
  63. sage/graphs/base/c_graph.pxd +4 -4
  64. sage/graphs/base/c_graph.pyx +270 -184
  65. sage/graphs/base/dense_graph.cpython-39-darwin.so +0 -0
  66. sage/graphs/base/graph_backends.cpython-39-darwin.so +0 -0
  67. sage/graphs/base/sparse_graph.cpython-39-darwin.so +0 -0
  68. sage/graphs/base/static_dense_graph.cpython-39-darwin.so +0 -0
  69. sage/graphs/base/static_sparse_backend.cpython-39-darwin.so +0 -0
  70. sage/graphs/base/static_sparse_backend.pyx +93 -6
  71. sage/graphs/base/static_sparse_graph.cpython-39-darwin.so +0 -0
  72. sage/graphs/base/static_sparse_graph.pyx +1 -1
  73. sage/graphs/bipartite_graph.py +0 -1
  74. sage/graphs/centrality.cpython-39-darwin.so +0 -0
  75. sage/graphs/centrality.pyx +0 -0
  76. sage/graphs/comparability.cpython-39-darwin.so +0 -0
  77. sage/graphs/comparability.pyx +172 -138
  78. sage/graphs/connectivity.cpython-39-darwin.so +0 -0
  79. sage/graphs/connectivity.pyx +194 -18
  80. sage/graphs/convexity_properties.cpython-39-darwin.so +0 -0
  81. sage/graphs/digraph_generators.py +118 -74
  82. sage/graphs/distances_all_pairs.cpython-39-darwin.so +0 -0
  83. sage/graphs/distances_all_pairs.pyx +145 -27
  84. sage/graphs/edge_connectivity.cpython-39-darwin.so +0 -0
  85. sage/graphs/generators/basic.py +471 -130
  86. sage/graphs/generators/distance_regular.cpython-39-darwin.so +0 -0
  87. sage/graphs/generators/distance_regular.pyx +12 -12
  88. sage/graphs/generators/families.py +2 -2
  89. sage/graphs/generators/random.py +8 -13
  90. sage/graphs/generators/smallgraphs.py +12 -11
  91. sage/graphs/generic_graph.py +687 -265
  92. sage/graphs/generic_graph_pyx.cpython-39-darwin.so +0 -0
  93. sage/graphs/genus.cpython-39-darwin.so +0 -0
  94. sage/graphs/graph.py +12 -46
  95. sage/graphs/graph_coloring.cpython-39-darwin.so +0 -0
  96. sage/graphs/graph_database.py +1 -1
  97. sage/graphs/graph_decompositions/bandwidth.cpython-39-darwin.so +0 -0
  98. sage/graphs/graph_decompositions/clique_separators.cpython-39-darwin.so +0 -0
  99. sage/graphs/graph_decompositions/cutwidth.cpython-39-darwin.so +0 -0
  100. sage/graphs/graph_decompositions/fast_digraph.cpython-39-darwin.so +0 -0
  101. sage/graphs/graph_decompositions/graph_products.cpython-39-darwin.so +0 -0
  102. sage/graphs/graph_decompositions/modular_decomposition.cpython-39-darwin.so +0 -0
  103. sage/graphs/graph_decompositions/slice_decomposition.cpython-39-darwin.so +0 -0
  104. sage/graphs/graph_decompositions/tree_decomposition.cpython-39-darwin.so +0 -0
  105. sage/graphs/graph_decompositions/vertex_separation.cpython-39-darwin.so +0 -0
  106. sage/graphs/graph_generators.py +110 -55
  107. sage/graphs/graph_generators_pyx.cpython-39-darwin.so +0 -0
  108. sage/graphs/graph_latex.py +1 -1
  109. sage/graphs/graph_list.py +2 -3
  110. sage/graphs/graph_plot.py +225 -30
  111. sage/graphs/hyperbolicity.cpython-39-darwin.so +0 -0
  112. sage/graphs/independent_sets.cpython-39-darwin.so +0 -0
  113. sage/graphs/isgci.py +3 -8
  114. sage/graphs/isoperimetric_inequalities.cpython-39-darwin.so +0 -0
  115. sage/graphs/line_graph.cpython-39-darwin.so +0 -0
  116. sage/graphs/matching.py +14 -25
  117. sage/graphs/matching_covered_graph.py +871 -60
  118. sage/graphs/orientations.py +190 -134
  119. sage/graphs/path_enumeration.cpython-39-darwin.so +0 -0
  120. sage/graphs/path_enumeration.pyx +25 -25
  121. sage/graphs/spanning_tree.cpython-39-darwin.so +0 -0
  122. sage/graphs/strongly_regular_db.cpython-39-darwin.so +0 -0
  123. sage/graphs/strongly_regular_db.pyx +54 -52
  124. sage/graphs/traversals.cpython-39-darwin.so +0 -0
  125. sage/graphs/traversals.pyx +114 -46
  126. sage/graphs/trees.cpython-39-darwin.so +0 -0
  127. sage/graphs/views.cpython-39-darwin.so +0 -0
  128. sage/graphs/weakly_chordal.cpython-39-darwin.so +0 -0
  129. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-39-darwin.so +0 -0
  130. sage/knots/free_knotinfo_monoid.py +2 -3
  131. sage/knots/knot.py +1 -1
  132. sage/knots/knotinfo.py +4 -4
  133. sage/knots/link.py +58 -57
  134. sage/sandpiles/sandpile.py +2 -3
  135. sage/topology/cell_complex.py +1 -1
  136. sage/topology/cubical_complex.py +7 -7
  137. sage/topology/delta_complex.py +4 -4
  138. sage/topology/simplicial_complex.py +7 -22
  139. passagemath_graphs-10.5.10.dist-info/RECORD +0 -251
  140. {passagemath_graphs-10.5.10.dist-info → passagemath_graphs-10.5.43.dist-info}/top_level.txt +0 -0
@@ -464,9 +464,11 @@ try:
464
464
  except ImportError:
465
465
  from sage.rings.real_double import RDF as Reals
466
466
 
467
+
467
468
  def _weight_if_real(x):
468
469
  return x if x in Reals else 1
469
470
 
471
+
470
472
  def _weight_1(x):
471
473
  return 1
472
474
 
@@ -776,20 +778,40 @@ class GenericGraph(GenericGraph_pyx):
776
778
 
777
779
  sage: G = graphs.CycleGraph(3)
778
780
  sage: H = G * 3; H
779
- Cycle graph disjoint_union Cycle graph disjoint_union Cycle graph: Graph on 9 vertices
781
+ Disjoint union of 3 copies of Cycle graph: Graph on 9 vertices
780
782
  sage: H.vertices(sort=True)
781
783
  [0, 1, 2, 3, 4, 5, 6, 7, 8]
782
784
  sage: H = G * 1; H
783
785
  Cycle graph: Graph on 3 vertices
786
+
787
+ TESTS::
788
+
789
+ sage: Graph(1) * -1
790
+ Traceback (most recent call last):
791
+ ...
792
+ TypeError: multiplication of a graph and a nonpositive integer is not defined
793
+ sage: Graph(1) * 2.5
794
+ Traceback (most recent call last):
795
+ ...
796
+ TypeError: multiplication of a graph and something other than an integer is not defined
784
797
  """
785
798
  if isinstance(n, (int, Integer)):
786
799
  if n < 1:
787
800
  raise TypeError('multiplication of a graph and a nonpositive integer is not defined')
788
801
  if n == 1:
789
802
  return copy(self)
790
- return sum([self] * (n - 1), self)
791
- else:
792
- raise TypeError('multiplication of a graph and something other than an integer is not defined')
803
+ ns = self.order()
804
+ ntot = n * ns
805
+ vint = {u: i for i, u in enumerate(self)}
806
+ edges = ((i, j, l) for u, v, l in self.edge_iterator()
807
+ for i, j in zip(range(vint[u], ntot, ns),
808
+ range(vint[v], ntot, ns)))
809
+ return self.__class__([range(ntot), edges], format='vertices_and_edges',
810
+ loops=self.allows_loops(),
811
+ multiedges=self.allows_multiple_edges(),
812
+ immutable=self.is_immutable(),
813
+ name=f"Disjoint union of {n} copies of {str(self)}")
814
+ raise TypeError('multiplication of a graph and something other than an integer is not defined')
793
815
 
794
816
  def __ne__(self, other):
795
817
  """
@@ -822,7 +844,7 @@ class GenericGraph(GenericGraph_pyx):
822
844
 
823
845
  sage: G = graphs.CycleGraph(3)
824
846
  sage: H = int(3) * G; H
825
- Cycle graph disjoint_union Cycle graph disjoint_union Cycle graph: Graph on 9 vertices
847
+ Disjoint union of 3 copies of Cycle graph: Graph on 9 vertices
826
848
  sage: H.vertices(sort=True)
827
849
  [0, 1, 2, 3, 4, 5, 6, 7, 8]
828
850
  """
@@ -5375,20 +5397,32 @@ class GenericGraph(GenericGraph_pyx):
5375
5397
 
5376
5398
  sage: g = graphs.PetersenGraph()
5377
5399
  sage: g.cycle_basis() # needs networkx, random (changes in networkx 3.2)
5378
- [[1, 6, 8, 5, 0], [4, 9, 6, 8, 5, 0], [7, 9, 6, 8, 5],
5379
- [4, 3, 8, 5, 0], [1, 2, 3, 8, 5, 0], [7, 2, 3, 8, 5]]
5400
+ [[6, 8, 5, 7, 9],
5401
+ [2, 3, 8, 5, 7],
5402
+ [4, 3, 8, 5, 7, 9],
5403
+ [4, 0, 5, 7, 9],
5404
+ [2, 1, 0, 5, 7],
5405
+ [6, 1, 0, 5, 7, 9]]
5380
5406
 
5381
5407
  One can also get the result as a list of lists of edges::
5382
5408
 
5383
5409
  sage: g.cycle_basis(output='edge') # needs networkx, random (changes in networkx 3.2)
5384
- [[(1, 6, None), (6, 8, None), (8, 5, None), (5, 0, None),
5385
- (0, 1, None)], [(4, 9, None), (9, 6, None), (6, 8, None),
5386
- (8, 5, None), (5, 0, None), (0, 4, None)], [(7, 9, None),
5387
- (9, 6, None), (6, 8, None), (8, 5, None), (5, 7, None)],
5388
- [(4, 3, None), (3, 8, None), (8, 5, None), (5, 0, None),
5389
- (0, 4, None)], [(1, 2, None), (2, 3, None), (3, 8, None),
5390
- (8, 5, None), (5, 0, None), (0, 1, None)], [(7, 2, None),
5391
- (2, 3, None), (3, 8, None), (8, 5, None), (5, 7, None)]]
5410
+ [[(6, 8, None), (8, 5, None), (5, 7, None), (7, 9, None), (9, 6, None)],
5411
+ [(2, 3, None), (3, 8, None), (8, 5, None), (5, 7, None), (7, 2, None)],
5412
+ [(4, 3, None),
5413
+ (3, 8, None),
5414
+ (8, 5, None),
5415
+ (5, 7, None),
5416
+ (7, 9, None),
5417
+ (9, 4, None)],
5418
+ [(4, 0, None), (0, 5, None), (5, 7, None), (7, 9, None), (9, 4, None)],
5419
+ [(2, 1, None), (1, 0, None), (0, 5, None), (5, 7, None), (7, 2, None)],
5420
+ [(6, 1, None),
5421
+ (1, 0, None),
5422
+ (0, 5, None),
5423
+ (5, 7, None),
5424
+ (7, 9, None),
5425
+ (9, 6, None)]]
5392
5426
 
5393
5427
  Checking the given cycles are algebraically free::
5394
5428
 
@@ -5469,6 +5503,17 @@ class GenericGraph(GenericGraph_pyx):
5469
5503
  [[(2, 3, 'c'), (3, 2, 'b')],
5470
5504
  [(3, 4, 'd'), (4, 1, 'f'), (1, 2, 'a'), (2, 3, 'b')],
5471
5505
  [(3, 4, 'e'), (4, 1, 'f'), (1, 2, 'a'), (2, 3, 'b')]]
5506
+
5507
+ Check that the method is valid for immutable graphs::
5508
+
5509
+ sage: G = Graph(graphs.CycleGraph(3), immutable=True)
5510
+ sage: G.cycle_basis() # needs networkx
5511
+ [[0, 1, 2]]
5512
+ sage: G = Graph([(1, 2, 'a'), (2, 3, 'b'), (2, 3, 'c'),
5513
+ ....: (3, 4, 'd'), (3, 4, 'e'), (4, 1, 'f')],
5514
+ ....: multiedges=True, immutable=True)
5515
+ sage: G.cycle_basis()
5516
+ [[3, 2], [4, 1, 2, 3], [4, 1, 2, 3]]
5472
5517
  """
5473
5518
  if output not in ['vertex', 'edge']:
5474
5519
  raise ValueError('output must be either vertex or edge')
@@ -5484,7 +5529,7 @@ class GenericGraph(GenericGraph_pyx):
5484
5529
 
5485
5530
  from sage.graphs.graph import Graph
5486
5531
  T = Graph(self.min_spanning_tree(), multiedges=True, format='list_of_edges')
5487
- H = self.copy()
5532
+ H = self.copy(immutable=False)
5488
5533
  H.delete_edges(T.edge_iterator())
5489
5534
  root = next(T.vertex_iterator())
5490
5535
  rank = dict(T.breadth_first_search(root, report_distance=True))
@@ -5564,9 +5609,9 @@ class GenericGraph(GenericGraph_pyx):
5564
5609
  sage: sorted(g.minimum_cycle_basis(by_weight=False))
5565
5610
  [[1, 2, 3], [1, 3, 4], [5, 6, 7]]
5566
5611
  sage: sorted(g.minimum_cycle_basis(by_weight=True, algorithm='NetworkX')) # needs networkx, random (changes in networkx 3.2)
5567
- [[1, 2, 3], [1, 2, 3, 4], [5, 6, 7]]
5612
+ [[2, 3, 1], [2, 3, 4, 1], [6, 7, 5]]
5568
5613
  sage: g.minimum_cycle_basis(by_weight=False, algorithm='NetworkX') # needs networkx, random (changes in networkx 3.2)
5569
- [[1, 2, 3], [1, 3, 4], [5, 6, 7]]
5614
+ [[3, 4, 1], [2, 3, 1], [6, 7, 5]]
5570
5615
 
5571
5616
  ::
5572
5617
 
@@ -5574,7 +5619,7 @@ class GenericGraph(GenericGraph_pyx):
5574
5619
  sage: sorted(g.minimum_cycle_basis(by_weight=False))
5575
5620
  [[1, 2, 3, 5], [3, 4, 5]]
5576
5621
  sage: sorted(g.minimum_cycle_basis(by_weight=False, algorithm='NetworkX')) # needs networkx, random (changes in networkx 3.2)
5577
- [[1, 2, 3, 5], [3, 4, 5]]
5622
+ [[3, 4, 5], [5, 3, 2, 1]]
5578
5623
 
5579
5624
  TESTS::
5580
5625
 
@@ -7033,7 +7078,7 @@ class GenericGraph(GenericGraph_pyx):
7033
7078
  if e:
7034
7079
  e = e.pop() # just one edge since self and its dual are simple
7035
7080
  edges.append([v1, v2, self.edge_label(e[0], e[1])])
7036
- return Graph([verts, edges])
7081
+ return Graph([verts, edges], format='vertices_and_edges')
7037
7082
 
7038
7083
  # Connectivity
7039
7084
 
@@ -8161,6 +8206,7 @@ class GenericGraph(GenericGraph_pyx):
8161
8206
  return val
8162
8207
 
8163
8208
  def longest_cycle(self, induced=False, use_edge_labels=False,
8209
+ immutable=None,
8164
8210
  solver=None, verbose=0, *, integrality_tolerance=0.001):
8165
8211
  r"""
8166
8212
  Return the longest (induced) cycle of ``self``.
@@ -8192,6 +8238,10 @@ class GenericGraph(GenericGraph_pyx):
8192
8238
  considered as a weight of `1`), or to compute a cycle with the largest
8193
8239
  possible number of edges (i.e., edge weights are set to 1)
8194
8240
 
8241
+ - ``immutable`` -- boolean (default: ``None``); whether to create a
8242
+ mutable/immutable (di)graph. ``immutable=None`` (default) means that
8243
+ the (di)graph and its longest cycle will behave the same way.
8244
+
8195
8245
  - ``solver`` -- string (default: ``None``); specifies a Mixed Integer
8196
8246
  Linear Programming (MILP) solver to be used. If set to ``None``, the
8197
8247
  default one is used. For more information on MILP solvers and which
@@ -8308,6 +8358,33 @@ class GenericGraph(GenericGraph_pyx):
8308
8358
  longest cycle from Subgraph of (Circuit disjoint_union Circuit): Digraph on 5 vertices
8309
8359
  sage: D.longest_cycle(induced=True)
8310
8360
  longest induced cycle from Subgraph of (Circuit disjoint_union Circuit): Digraph on 5 vertices
8361
+
8362
+ Check the behavior of parameter ``immutable``::
8363
+
8364
+ sage: # needs sage.numerical.mip
8365
+ sage: G = Graph([(0, 1), (1, 2), (0, 2)], immutable=True)
8366
+ sage: G.longest_cycle().is_immutable()
8367
+ True
8368
+ sage: G = graphs.Grid2dGraph(3, 4)
8369
+ sage: G.longest_cycle(induced=False, immutable=True).is_immutable()
8370
+ True
8371
+ sage: G.longest_cycle(induced=True, immutable=True).is_immutable()
8372
+ True
8373
+ sage: D = digraphs.Circuit(15)
8374
+ sage: for u, v in D.edges(labels=False):
8375
+ ....: D.set_edge_label(u, v, 1)
8376
+ sage: D.add_edge(0, 10, 50)
8377
+ sage: D.add_edge(11, 1, 1)
8378
+ sage: D.add_edge(13, 0, 1)
8379
+ sage: D.longest_cycle(induced=False, use_edge_labels=False, immutable=True).is_immutable()
8380
+ True
8381
+ sage: D.longest_cycle(induced=False, use_edge_labels=True, immutable=True)[1].is_immutable()
8382
+ True
8383
+ sage: D = DiGraph(D, immutable=True)
8384
+ sage: D.longest_cycle(induced=False, use_edge_labels=True)[1].is_immutable()
8385
+ True
8386
+ sage: D.longest_cycle(induced=False, use_edge_labels=True, immutable=False)[1].is_immutable()
8387
+ False
8311
8388
  """
8312
8389
  self._scream_if_not_simple()
8313
8390
  G = self
@@ -8329,7 +8406,8 @@ class GenericGraph(GenericGraph_pyx):
8329
8406
  return gg.order()
8330
8407
 
8331
8408
  directed = G.is_directed()
8332
- immutable = G.is_immutable()
8409
+ if immutable is None:
8410
+ immutable = G.is_immutable()
8333
8411
  if directed:
8334
8412
  from sage.graphs.digraph import DiGraph as MyGraph
8335
8413
  blocks = G.strongly_connected_components()
@@ -8347,6 +8425,7 @@ class GenericGraph(GenericGraph_pyx):
8347
8425
  h = G.subgraph(vertices=block)
8348
8426
  C = h.longest_cycle(induced=induced,
8349
8427
  use_edge_labels=use_edge_labels,
8428
+ immutable=immutable,
8350
8429
  solver=solver, verbose=verbose,
8351
8430
  integrality_tolerance=integrality_tolerance)
8352
8431
  if total_weight(C) > best_w:
@@ -8365,8 +8444,7 @@ class GenericGraph(GenericGraph_pyx):
8365
8444
  return MyGraph(name=name, immutable=immutable)
8366
8445
  if (not induced and ((directed and G.order() == 2) or
8367
8446
  (not directed and G.order() == 3))):
8368
- answer = G.copy()
8369
- answer.name(name)
8447
+ answer = MyGraph(G, immutable=immutable, name=name)
8370
8448
  if use_edge_labels:
8371
8449
  return total_weight(answer), answer
8372
8450
  return answer
@@ -8485,7 +8563,8 @@ class GenericGraph(GenericGraph_pyx):
8485
8563
  best.set_pos({u: pp for u, pp in G.get_pos().items() if u in best})
8486
8564
  return (best_w, best) if use_edge_labels else best
8487
8565
 
8488
- def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm='MILP',
8566
+ def longest_path(self, s=None, t=None, use_edge_labels=False,
8567
+ algorithm='MILP', immutable=None,
8489
8568
  solver=None, verbose=0, *, integrality_tolerance=1e-3):
8490
8569
  r"""
8491
8570
  Return a longest path of ``self``.
@@ -8526,6 +8605,10 @@ class GenericGraph(GenericGraph_pyx):
8526
8605
  parameters ``s``, ``t`` and ``use_edge_labels``. An error is raised
8527
8606
  if these parameters are set.
8528
8607
 
8608
+ - ``immutable`` -- boolean (default: ``None``); whether to create a
8609
+ mutable/immutable (di)graph. ``immutable=None`` (default) means that
8610
+ the (di)graph and its longest path will behave the same way.
8611
+
8529
8612
  - ``solver`` -- string (default: ``None``); specifies a Mixed Integer
8530
8613
  Linear Programming (MILP) solver to be used. If set to ``None``, the
8531
8614
  default one is used. For more information on MILP solvers and which
@@ -8693,6 +8776,24 @@ class GenericGraph(GenericGraph_pyx):
8693
8776
  ...
8694
8777
  ValueError: parameters s, t, and use_edge_labels can not be used in
8695
8778
  combination with algorithm 'heuristic'
8779
+
8780
+ Check the behavior of parameter ``immutable``::
8781
+
8782
+ sage: # needs sage.numerical.mip
8783
+ sage: g1 = digraphs.RandomDirectedGNP(15, 0.2)
8784
+ sage: for u,v in g.edge_iterator(labels=False):
8785
+ ....: g.set_edge_label(u, v, random())
8786
+ sage: g2 = DiGraph(2 * g1, immutable=True)
8787
+ sage: lp1 = g1.longest_path(use_edge_labels=True)
8788
+ sage: lp2 = g2.longest_path(use_edge_labels=True)
8789
+ sage: lp1[0] == lp2[0]
8790
+ True
8791
+ sage: not lp1[1].is_immutable() and lp2[1].is_immutable()
8792
+ True
8793
+ sage: lp1 = g1.longest_path(use_edge_labels=True, immutable=True)
8794
+ sage: lp2 = g2.longest_path(use_edge_labels=True, immutable=False)
8795
+ sage: lp1[1].is_immutable() and not lp2[1].is_immutable()
8796
+ True
8696
8797
  """
8697
8798
  self._scream_if_not_simple()
8698
8799
 
@@ -8708,16 +8809,19 @@ class GenericGraph(GenericGraph_pyx):
8708
8809
  raise ValueError("parameters s, t, and use_edge_labels can not "
8709
8810
  "be used in combination with algorithm 'heuristic'")
8710
8811
 
8812
+ if immutable is None:
8813
+ immutable = self.is_immutable()
8814
+
8711
8815
  # Quick improvement
8712
8816
  if not self.is_connected():
8713
8817
  if use_edge_labels:
8714
- return max((g.longest_path(s=s, t=t,
8818
+ return max((g.longest_path(s=s, t=t, immutable=immutable,
8715
8819
  use_edge_labels=use_edge_labels,
8716
8820
  algorithm=algorithm)
8717
8821
  for g in self.connected_components_subgraphs()),
8718
8822
  key=lambda x: x[0])
8719
8823
 
8720
- return max((g.longest_path(s=s, t=t,
8824
+ return max((g.longest_path(s=s, t=t, immutable=immutable,
8721
8825
  use_edge_labels=use_edge_labels,
8722
8826
  algorithm=algorithm)
8723
8827
  for g in self.connected_components_subgraphs()),
@@ -8746,16 +8850,18 @@ class GenericGraph(GenericGraph_pyx):
8746
8850
  (self._directed and (s is not None) and (t is not None) and
8747
8851
  not self.shortest_path(s, t))):
8748
8852
  if self._directed:
8749
- from sage.graphs.digraph import DiGraph
8750
- return [0, DiGraph()] if use_edge_labels else DiGraph()
8751
- from sage.graphs.graph import Graph
8752
- return [0, Graph()] if use_edge_labels else Graph()
8853
+ from sage.graphs.digraph import DiGraph as MyGraph
8854
+ else:
8855
+ from sage.graphs.graph import Graph as MyGraph
8856
+ GG = MyGraph(immutable=immutable)
8857
+ return [0, GG] if use_edge_labels else GG
8753
8858
 
8754
8859
  # Calling the heuristic if asked
8755
8860
  if algorithm == "heuristic":
8756
8861
  from sage.graphs.generic_graph_pyx import find_hamiltonian as fh
8757
8862
  x = fh(self, find_path=True)[1]
8758
- return self.subgraph(vertices=x, edges=list(zip(x[:-1], x[1:])))
8863
+ return self.subgraph(vertices=x, edges=list(zip(x[:-1], x[1:])),
8864
+ immutable=immutable)
8759
8865
 
8760
8866
  ##################
8761
8867
  # LP Formulation #
@@ -8885,21 +8991,20 @@ class GenericGraph(GenericGraph_pyx):
8885
8991
  edge_used = p.get_values(edge_used, convert=bool, tolerance=integrality_tolerance)
8886
8992
  vertex_used = p.get_values(vertex_used, convert=bool, tolerance=integrality_tolerance)
8887
8993
  if self._directed:
8888
- g = self.subgraph(vertices=(v for v in self if vertex_used[v]),
8889
- edges=((u, v, l) for u, v, l in self.edge_iterator()
8890
- if edge_used[u, v]))
8994
+ edges = ((u, v, l) for u, v, l in self.edge_iterator()
8995
+ if edge_used[u, v])
8891
8996
  else:
8892
- g = self.subgraph(
8893
- vertices=(v for v in self if vertex_used[v]),
8894
- edges=((u, v, l) for u, v, l in self.edge_iterator()
8895
- if edge_used[frozenset((u, v))]))
8997
+ edges = ((u, v, l) for u, v, l in self.edge_iterator()
8998
+ if edge_used[frozenset((u, v))])
8999
+ g = self.subgraph(vertices=(v for v in self if vertex_used[v]),
9000
+ edges=edges, immutable=immutable)
8896
9001
  if use_edge_labels:
8897
9002
  return sum(map(weight, g.edge_labels())), g
8898
9003
  return g
8899
9004
 
8900
9005
  def hamiltonian_path(self, s=None, t=None, use_edge_labels=False,
8901
- maximize=False, algorithm='MILP', solver=None, verbose=0,
8902
- *, integrality_tolerance=1e-3):
9006
+ maximize=False, algorithm='MILP', immutable=None,
9007
+ solver=None, verbose=0, *, integrality_tolerance=1e-3):
8903
9008
  r"""
8904
9009
  Return a Hamiltonian path of the current graph/digraph.
8905
9010
 
@@ -8945,6 +9050,10 @@ class GenericGraph(GenericGraph_pyx):
8945
9050
  * The backtrack algorithm does not support edge weighting, so setting
8946
9051
  ``use_edge_labels=True`` will force the use of the MILP algorithm.
8947
9052
 
9053
+ - ``immutable`` -- boolean (default: ``None``); whether to create a
9054
+ mutable/immutable (di)graph. ``immutable=None`` (default) means that
9055
+ the (di)graph and its hamiltonian path will behave the same way.
9056
+
8948
9057
  - ``solver`` -- string (default: ``None``); specifies a Mixed Integer
8949
9058
  Linear Programming (MILP) solver to be used. If set to ``None``, the
8950
9059
  default one is used. For more information on MILP solvers and which
@@ -9030,6 +9139,20 @@ class GenericGraph(GenericGraph_pyx):
9030
9139
  Traceback (most recent call last):
9031
9140
  ...
9032
9141
  ValueError: algorithm must be either 'backtrack' or 'MILP'
9142
+
9143
+ Check the behavior of parameter ``immutable``::
9144
+
9145
+ sage: # needs sage.numerical.mip
9146
+ sage: g = graphs.Grid2dGraph(3, 3)
9147
+ sage: g.hamiltonian_path().is_immutable()
9148
+ False
9149
+ sage: g.hamiltonian_path(immutable=True).is_immutable()
9150
+ True
9151
+ sage: g = Graph(g, immutable=True)
9152
+ sage: g.hamiltonian_path().is_immutable()
9153
+ True
9154
+ sage: g.hamiltonian_path(use_edge_labels=True)[1].is_immutable()
9155
+ True
9033
9156
  """
9034
9157
  if use_edge_labels or algorithm is None:
9035
9158
  # We force the algorithm to 'MILP'
@@ -9044,6 +9167,8 @@ class GenericGraph(GenericGraph_pyx):
9044
9167
  if not self.is_connected():
9045
9168
  return (0, None) if use_edge_labels else None
9046
9169
 
9170
+ if immutable is None:
9171
+ immutable = self.is_immutable()
9047
9172
  #
9048
9173
  # Deal with loops and multiple edges
9049
9174
  #
@@ -9107,7 +9232,8 @@ class GenericGraph(GenericGraph_pyx):
9107
9232
  new_t = ones.pop()
9108
9233
 
9109
9234
  if not use_edge_labels and algorithm == "backtrack":
9110
- path = g.longest_path(s=new_s, t=new_t, algorithm='backtrack')
9235
+ path = g.longest_path(s=new_s, t=new_t, algorithm='backtrack',
9236
+ immutable=immutable)
9111
9237
  return path if path.order() == g.order() else None
9112
9238
 
9113
9239
  #
@@ -9154,6 +9280,8 @@ class GenericGraph(GenericGraph_pyx):
9154
9280
 
9155
9281
  tsp.delete_vertices(extra_vertices)
9156
9282
  tsp.name("Hamiltonian path from {}".format(self.name()))
9283
+ if immutable:
9284
+ tsp = tsp.copy(immutable=True)
9157
9285
 
9158
9286
  def weight(label):
9159
9287
  return 1 if label is None else label
@@ -10787,7 +10915,7 @@ class GenericGraph(GenericGraph_pyx):
10787
10915
  solvers over an inexact base ring; see
10788
10916
  :meth:`MixedIntegerLinearProgram.get_values`.
10789
10917
 
10790
- Only useful when parameter ``ìnteger`` is ``True``.
10918
+ Only useful when parameter ``integer`` is ``True``.
10791
10919
 
10792
10920
  ALGORITHM:
10793
10921
 
@@ -17359,7 +17487,7 @@ class GenericGraph(GenericGraph_pyx):
17359
17487
  sage: D.shortest_path(4, 8, algorithm='Dijkstra_Bid_NetworkX') # needs networkx
17360
17488
  [4, 3, 2, 1, 8]
17361
17489
  sage: D.shortest_path(4, 9, algorithm='Dijkstra_Bid')
17362
- [4, 3, 19, 0, 10, 9]
17490
+ [4, 3, 2, 1, 8, 9]
17363
17491
  sage: D.shortest_path(5, 5)
17364
17492
  [5]
17365
17493
  sage: D.delete_edges(D.edges_incident(13))
@@ -18875,7 +19003,8 @@ class GenericGraph(GenericGraph_pyx):
18875
19003
 
18876
19004
  def breadth_first_search(self, start, ignore_direction=False,
18877
19005
  distance=None, neighbors=None,
18878
- report_distance=False, edges=False):
19006
+ report_distance=False, edges=False,
19007
+ forbidden_vertices=None):
18879
19008
  """
18880
19009
  Return an iterator over the vertices in a breadth-first ordering.
18881
19010
 
@@ -18910,6 +19039,9 @@ class GenericGraph(GenericGraph_pyx):
18910
19039
  Note that parameters ``edges`` and ``report_distance`` cannot be
18911
19040
  ``True`` simultaneously.
18912
19041
 
19042
+ - ``forbidden_vertices`` -- list (default: ``None``); set of vertices to
19043
+ avoid during the search. The start vertex ``v`` cannot be in this set.
19044
+
18913
19045
  .. SEEALSO::
18914
19046
 
18915
19047
  - :meth:`breadth_first_search <sage.graphs.base.c_graph.CGraphBackend.breadth_first_search>`
@@ -18998,6 +19130,12 @@ class GenericGraph(GenericGraph_pyx):
18998
19130
  sage: list(D.breadth_first_search(1, edges=True))
18999
19131
  [(1, 2), (1, 3), (2, 4)]
19000
19132
 
19133
+ BFS in a graph with forbidden vertices::
19134
+
19135
+ sage: G = graphs.PetersenGraph()
19136
+ sage: list(G.breadth_first_search(0, forbidden_vertices=[1, 2]))
19137
+ [0, 4, 5, 3, 9, 7, 8, 6]
19138
+
19001
19139
  TESTS::
19002
19140
 
19003
19141
  sage: D = DiGraph({1: [0], 2: [0]})
@@ -19016,6 +19154,18 @@ class GenericGraph(GenericGraph_pyx):
19016
19154
  Traceback (most recent call last):
19017
19155
  ...
19018
19156
  ValueError: parameters edges and report_distance cannot be ``True`` simultaneously
19157
+ sage: list(G.breadth_first_search(0, forbidden_vertices=[0]))
19158
+ Traceback (most recent call last):
19159
+ ...
19160
+ ValueError: the start vertex is in the set of forbidden vertices
19161
+ sage: list(G.breadth_first_search(0, forbidden_vertices=[0], distance=2))
19162
+ Traceback (most recent call last):
19163
+ ...
19164
+ ValueError: the start vertex is in the set of forbidden vertices
19165
+ sage: list(G.breadth_first_search([0, 1], forbidden_vertices=[1]))
19166
+ Traceback (most recent call last):
19167
+ ...
19168
+ ValueError: start vertex 1 is in the set of forbidden vertices
19019
19169
  """
19020
19170
  from sage.rings.semirings.non_negative_integer_semiring import NN
19021
19171
  if (distance is not None and distance not in NN):
@@ -19029,17 +19179,23 @@ class GenericGraph(GenericGraph_pyx):
19029
19179
  and hasattr(self._backend, "breadth_first_search")):
19030
19180
  yield from self._backend.breadth_first_search(
19031
19181
  start, ignore_direction=ignore_direction,
19032
- report_distance=report_distance, edges=edges)
19182
+ report_distance=report_distance, edges=edges,
19183
+ forbidden_vertices=forbidden_vertices)
19033
19184
  else:
19034
19185
  if neighbors is None:
19035
19186
  if not self._directed or ignore_direction:
19036
19187
  neighbors = self.neighbor_iterator
19037
19188
  else:
19038
19189
  neighbors = self.neighbor_out_iterator
19039
- seen = set()
19190
+ seen = set() if forbidden_vertices is None else set(forbidden_vertices)
19040
19191
  if isinstance(start, list):
19192
+ for s in start:
19193
+ if s in seen:
19194
+ raise ValueError(f"start vertex {s} is in the set of forbidden vertices")
19041
19195
  queue = [(v, 0) for v in start]
19042
19196
  else:
19197
+ if start in seen:
19198
+ raise ValueError("the start vertex is in the set of forbidden vertices")
19043
19199
  queue = [(start, 0)]
19044
19200
 
19045
19201
  # Non-existing start vertex is detected later if distance > 0.
@@ -19071,7 +19227,7 @@ class GenericGraph(GenericGraph_pyx):
19071
19227
  yield w
19072
19228
 
19073
19229
  def depth_first_search(self, start, ignore_direction=False,
19074
- neighbors=None, edges=False):
19230
+ neighbors=None, edges=False, forbidden_vertices=None):
19075
19231
  """
19076
19232
  Return an iterator over the vertices in a depth-first ordering.
19077
19233
 
@@ -19094,6 +19250,9 @@ class GenericGraph(GenericGraph_pyx):
19094
19250
  of the DFS tree in the order of visit or the vertices (default).
19095
19251
  Edges are directed in root to leaf orientation of the tree.
19096
19252
 
19253
+ - ``forbidden_vertices`` -- list (default: ``None``); set of vertices to
19254
+ avoid during the search. The start vertex ``v`` cannot be in this set.
19255
+
19097
19256
  .. SEEALSO::
19098
19257
 
19099
19258
  - :meth:`breadth_first_search`
@@ -19153,6 +19312,12 @@ class GenericGraph(GenericGraph_pyx):
19153
19312
  sage: list(D.depth_first_search(2, edges=True, ignore_direction=True))
19154
19313
  [(2, 3), (3, 4), (2, 1), (1, 0)]
19155
19314
 
19315
+ DFS in a graph with forbidden vertices::
19316
+
19317
+ sage: G = graphs.PetersenGraph()
19318
+ sage: list(G.depth_first_search(0, forbidden_vertices=[1, 2]))
19319
+ [0, 5, 8, 6, 9, 7, 4, 3]
19320
+
19156
19321
  TESTS::
19157
19322
 
19158
19323
  sage: D = DiGraph({1: [0], 2: [0]})
@@ -19176,22 +19341,40 @@ class GenericGraph(GenericGraph_pyx):
19176
19341
  [1, 3, 6, 4, 5, 7, 2]
19177
19342
  sage: list(D.depth_first_search(1, ignore_direction=True, edges=True))
19178
19343
  [(1, 3), (3, 6), (6, 7), (7, 5), (5, 4), (1, 2)]
19344
+ sage: list(G.depth_first_search(0, forbidden_vertices=[0]))
19345
+ Traceback (most recent call last):
19346
+ ...
19347
+ ValueError: the start vertex is in the set of forbidden vertices
19348
+ sage: list(G.depth_first_search(0, forbidden_vertices=[0], edges=True))
19349
+ Traceback (most recent call last):
19350
+ ...
19351
+ ValueError: the start vertex is in the set of forbidden vertices
19352
+ sage: list(G.depth_first_search([0, 1], forbidden_vertices=[1]))
19353
+ Traceback (most recent call last):
19354
+ ...
19355
+ ValueError: start vertex 1 is in the set of forbidden vertices
19179
19356
  """
19180
19357
  # Preferably use the Cython implementation
19181
19358
  if (neighbors is None and not isinstance(start, list)
19182
19359
  and hasattr(self._backend, "depth_first_search") and not edges):
19183
- yield from self._backend.depth_first_search(start, ignore_direction=ignore_direction)
19360
+ yield from self._backend.depth_first_search(start, ignore_direction=ignore_direction,
19361
+ forbidden_vertices=forbidden_vertices)
19184
19362
  else:
19185
19363
  if neighbors is None:
19186
19364
  if not self._directed or ignore_direction:
19187
19365
  neighbors = self.neighbor_iterator
19188
19366
  else:
19189
19367
  neighbors = self.neighbor_out_iterator
19190
- seen = set()
19368
+ seen = set(forbidden_vertices) if forbidden_vertices else set()
19191
19369
  if isinstance(start, list):
19370
+ for s in start:
19371
+ if s in seen:
19372
+ raise ValueError(f"start vertex {s} is in the set of forbidden vertices")
19192
19373
  # Reverse the list so that the initial vertices come out in the same order
19193
19374
  queue = [(v, 0) for v in reversed(start)]
19194
19375
  else:
19376
+ if start in seen:
19377
+ raise ValueError("the start vertex is in the set of forbidden vertices")
19195
19378
  queue = [(start, 0)]
19196
19379
 
19197
19380
  if not edges:
@@ -19292,7 +19475,16 @@ class GenericGraph(GenericGraph_pyx):
19292
19475
  sage: D.add_clique(range(4), loops=True)
19293
19476
  sage: D.is_clique(directed_clique=True, loops=True)
19294
19477
  True
19478
+
19479
+ Immutable graph::
19480
+
19481
+ sage: Graph(immutable=True).add_clique([1, 2, 3])
19482
+ Traceback (most recent call last):
19483
+ ...
19484
+ ValueError: graph is immutable; please change a copy instead (use function copy())
19295
19485
  """
19486
+ if self.is_immutable():
19487
+ raise ValueError("graph is immutable; please change a copy instead (use function copy())")
19296
19488
  import itertools
19297
19489
  if loops:
19298
19490
  if self.is_directed():
@@ -19358,6 +19550,13 @@ class GenericGraph(GenericGraph_pyx):
19358
19550
  sage: G.add_cycle(['a', 'b', 'c'])
19359
19551
  sage: G.order(), G.size()
19360
19552
  (3, 3)
19553
+
19554
+ Immutable graph::
19555
+
19556
+ sage: Graph(immutable=True).add_cycle([1, 2, 3])
19557
+ Traceback (most recent call last):
19558
+ ...
19559
+ ValueError: graph is immutable; please change a copy instead (use function copy())
19361
19560
  """
19362
19561
  if vertices:
19363
19562
  self.add_path(vertices)
@@ -19394,13 +19593,23 @@ class GenericGraph(GenericGraph_pyx):
19394
19593
  sage: D.add_path(list(range(4)))
19395
19594
  sage: D.edges(sort=True)
19396
19595
  [(0, 1, None), (1, 2, None), (2, 3, None)]
19596
+
19597
+ TESTS:
19598
+
19599
+ Immutable graph::
19600
+
19601
+ sage: Graph(immutable=True).add_path([])
19602
+ sage: Graph(immutable=True).add_path([1, 2, 3])
19603
+ Traceback (most recent call last):
19604
+ ...
19605
+ ValueError: graph is immutable; please change a copy instead (use function copy())
19397
19606
  """
19398
19607
  if not vertices:
19399
19608
  return
19400
19609
  self.add_vertices(vertices)
19401
19610
  self.add_edges(zip(vertices[:-1], vertices[1:]))
19402
19611
 
19403
- def complement(self):
19612
+ def complement(self, immutable=None):
19404
19613
  """
19405
19614
  Return the complement of the (di)graph.
19406
19615
 
@@ -19408,6 +19617,12 @@ class GenericGraph(GenericGraph_pyx):
19408
19617
  that are not in the original graph. This is not well defined for graphs
19409
19618
  with multiple edges.
19410
19619
 
19620
+ INPUT:
19621
+
19622
+ - ``immutable`` -- boolean (default: ``None``); whether to return a
19623
+ mutable or an immutable version of ``self``. By default (``None``),
19624
+ the graph and its complement behave the same.
19625
+
19411
19626
  EXAMPLES::
19412
19627
 
19413
19628
  sage: P = graphs.PetersenGraph()
@@ -19449,6 +19664,17 @@ class GenericGraph(GenericGraph_pyx):
19449
19664
  Graph on 10 vertices
19450
19665
  sage: g.complement()
19451
19666
  Graph on 10 vertices
19667
+
19668
+ Check the behavior of parameter ``immutable``::
19669
+
19670
+ sage: type(Graph().complement()._backend)
19671
+ <class 'sage.graphs.base.dense_graph.DenseGraphBackend'>
19672
+ sage: type(Graph().complement(immutable=True)._backend)
19673
+ <class 'sage.graphs.base.static_sparse_backend.StaticSparseBackend'>
19674
+ sage: type(Graph(immutable=True).complement()._backend)
19675
+ <class 'sage.graphs.base.static_sparse_backend.StaticSparseBackend'>
19676
+ sage: type(Graph(immutable=True).complement(immutable=False)._backend)
19677
+ <class 'sage.graphs.base.dense_graph.DenseGraphBackend'>
19452
19678
  """
19453
19679
  self._scream_if_not_simple()
19454
19680
 
@@ -19457,8 +19683,9 @@ class GenericGraph(GenericGraph_pyx):
19457
19683
 
19458
19684
  if self.name():
19459
19685
  G.name("complement({})".format(self.name()))
19460
-
19461
- if self.is_immutable():
19686
+ if immutable is None:
19687
+ immutable = self.is_immutable()
19688
+ if immutable:
19462
19689
  return G.copy(immutable=True)
19463
19690
  return G
19464
19691
 
@@ -19512,12 +19739,31 @@ class GenericGraph(GenericGraph_pyx):
19512
19739
  [(2, 3, 1), (3, 2, None)]
19513
19740
  sage: G.to_simple(to_undirected=False, keep_label='max').edges(sort=True)
19514
19741
  [(2, 3, 2), (3, 2, None)]
19742
+
19743
+ TESTS:
19744
+
19745
+ Check the behavior of parameter ``immutable``::
19746
+
19747
+ sage: G = Graph([(0, 0), (0, 1)] * 2, loops=True, multiedges=True, immutable=True)
19748
+ sage: H = G.to_simple()
19749
+ sage: H.is_immutable()
19750
+ True
19751
+ sage: H.edges(labels=False)
19752
+ [(0, 1)]
19753
+ sage: H = G.to_simple(immutable=False)
19754
+ sage: H.is_immutable()
19755
+ False
19756
+ sage: G = Graph([(0, 0), (0, 1)] * 2, loops=True, multiedges=True, immutable=False)
19757
+ sage: G.to_simple().is_immutable()
19758
+ False
19759
+ sage: G.to_simple(immutable=True).is_immutable()
19760
+ True
19515
19761
  """
19516
19762
  if to_undirected:
19517
19763
  from sage.graphs.graph import Graph
19518
- g = Graph(self)
19764
+ g = Graph(self, immutable=False)
19519
19765
  else:
19520
- g = copy(self)
19766
+ g = self.copy(immutable=False)
19521
19767
  g.allow_loops(False)
19522
19768
  g.allow_multiple_edges(False, keep_label=keep_label)
19523
19769
  if immutable is None:
@@ -19574,8 +19820,27 @@ class GenericGraph(GenericGraph_pyx):
19574
19820
  Custom path disjoint_union Cycle graph: Graph on 5 vertices
19575
19821
  sage: J.vertices(sort=True)
19576
19822
  [(0, 'a'), (0, 'b'), (1, 0), (1, 1), (1, 2)]
19823
+
19824
+ TESTS:
19825
+
19826
+ Check the behavior of parameter ``immutable``::
19827
+
19828
+ sage: G = Graph([(0, 1)])
19829
+ sage: G.disjoint_union(G).is_immutable()
19830
+ False
19831
+ sage: G.disjoint_union(G, immutable=True).is_immutable()
19832
+ True
19833
+ sage: H = G.copy(immutable=True)
19834
+ sage: H.disjoint_union(H).is_immutable()
19835
+ True
19836
+ sage: G.disjoint_union(G, immutable=False).is_immutable()
19837
+ False
19838
+ sage: H.disjoint_union(G).is_immutable()
19839
+ False
19840
+ sage: G.disjoint_union(G, immutable=True).is_immutable()
19841
+ True
19577
19842
  """
19578
- if (self._directed and not other._directed) or (not self._directed and other._directed):
19843
+ if self._directed != other._directed:
19579
19844
  raise TypeError('both arguments must be of the same class')
19580
19845
 
19581
19846
  if labels not in ['pairs', 'integers']:
@@ -19587,7 +19852,11 @@ class GenericGraph(GenericGraph_pyx):
19587
19852
  else:
19588
19853
  r_self = {v: (0, v) for v in self}
19589
19854
  r_other = {v: (1, v) for v in other}
19590
- G = self.relabel(r_self, inplace=False).union(other.relabel(r_other, inplace=False), immutable=immutable)
19855
+
19856
+ from itertools import chain
19857
+ vertices = chain(r_self.values(), r_other.values())
19858
+ edges = chain(((r_self[u], r_self[v], w) for u, v, w in self.edge_iterator()),
19859
+ ((r_other[u], r_other[v], w) for u, v, w in other.edge_iterator()))
19591
19860
 
19592
19861
  a = self.name()
19593
19862
  if not a:
@@ -19595,8 +19864,22 @@ class GenericGraph(GenericGraph_pyx):
19595
19864
  b = other.name()
19596
19865
  if not b:
19597
19866
  b = other._repr_()
19598
- G._name = '{} disjoint_union {}'.format(a, b)
19599
- return G
19867
+ name = f"{a} disjoint_union {b}"
19868
+
19869
+ multiedges = self.allows_multiple_edges() or other.allows_multiple_edges()
19870
+ loops = self.allows_loops() or other.allows_loops()
19871
+ weighted = self.weighted() and other.weighted()
19872
+ if immutable is None:
19873
+ immutable = self.is_immutable() and other.is_immutable()
19874
+
19875
+ if self._directed:
19876
+ from sage.graphs.digraph import DiGraph as GT
19877
+ else:
19878
+ from sage.graphs.graph import Graph as GT
19879
+
19880
+ return GT([vertices, edges], format='vertices_and_edges',
19881
+ weighted=weighted, loops=loops, multiedges=multiedges,
19882
+ name=name, immutable=immutable)
19600
19883
 
19601
19884
  def union(self, other, immutable=None):
19602
19885
  """
@@ -19673,32 +19956,27 @@ class GenericGraph(GenericGraph_pyx):
19673
19956
  sage: D1.union(D2).weighted() or D2.union(D1).weighted()
19674
19957
  False
19675
19958
  """
19676
- if (self._directed and not other._directed) or (not self._directed and other._directed):
19959
+ if self._directed != other._directed:
19677
19960
  raise TypeError('both arguments must be of the same class')
19678
19961
 
19679
19962
  multiedges = self.allows_multiple_edges() or other.allows_multiple_edges()
19680
19963
  loops = self.allows_loops() or other.allows_loops()
19681
19964
  weighted = self.weighted() and other.weighted()
19965
+ if immutable is None:
19966
+ immutable = self.is_immutable() and other.is_immutable()
19682
19967
 
19683
19968
  if self._directed:
19684
- from sage.graphs.digraph import DiGraph
19685
- G = DiGraph(multiedges=multiedges, loops=loops, weighted=weighted)
19969
+ from sage.graphs.digraph import DiGraph as GT
19686
19970
  else:
19687
- from sage.graphs.graph import Graph
19688
- G = Graph(multiedges=multiedges, loops=loops, weighted=weighted)
19689
- G.add_vertices(self)
19690
- G.add_vertices(other)
19691
- G.add_edges(self.edge_iterator())
19692
- G.add_edges(other.edge_iterator())
19971
+ from sage.graphs.graph import Graph as GT
19693
19972
 
19694
- if immutable is None:
19695
- immutable = self.is_immutable() and other.is_immutable()
19696
- if immutable:
19697
- G = G.copy(immutable=True)
19698
-
19699
- return G
19973
+ from itertools import chain
19974
+ return GT([chain(self, other),
19975
+ chain(self.edge_iterator(), other.edge_iterator())],
19976
+ format='vertices_and_edges', weighted=weighted, loops=loops,
19977
+ multiedges=multiedges, immutable=immutable)
19700
19978
 
19701
- def cartesian_product(self, other):
19979
+ def cartesian_product(self, other, immutable=None):
19702
19980
  r"""
19703
19981
  Return the Cartesian product of ``self`` and ``other``.
19704
19982
 
@@ -19707,6 +19985,15 @@ class GenericGraph(GenericGraph_pyx):
19707
19985
  and `((u,v), (w,x))` is an edge iff either - `(u, w)` is an edge of self
19708
19986
  and `v = x`, or - `(v, x)` is an edge of other and `u = w`.
19709
19987
 
19988
+ INPUT:
19989
+
19990
+ - ``other`` -- a graph or a digraph
19991
+
19992
+ - ``immutable`` -- boolean (default: ``None``); whether to create a
19993
+ mutable/immutable product. ``immutable=None`` (default) means that the
19994
+ graphs and their product will behave the same way. If only one of them
19995
+ is immutable, the product will be mutable.
19996
+
19710
19997
  .. SEEALSO::
19711
19998
 
19712
19999
  - :meth:`~sage.graphs.graph_decompositions.graph_products.is_cartesian_product`
@@ -19759,27 +20046,48 @@ class GenericGraph(GenericGraph_pyx):
19759
20046
  sage: V = Q.strongly_connected_component_containing_vertex((0, 'aa')) # needs sage.combinat
19760
20047
  sage: B.is_isomorphic(Q.subgraph(V)) # needs sage.combinat
19761
20048
  True
20049
+
20050
+ Check the behavior of parameter ``immutable``::
20051
+
20052
+ sage: A = Graph([(0, 1)])
20053
+ sage: B = Graph([('a', 'b')], immutable=True)
20054
+ sage: A.cartesian_product(A).is_immutable()
20055
+ False
20056
+ sage: A.cartesian_product(A, immutable=True).is_immutable()
20057
+ True
20058
+ sage: A.cartesian_product(B).is_immutable()
20059
+ False
20060
+ sage: A.cartesian_product(B, immutable=True).is_immutable()
20061
+ True
20062
+ sage: B.cartesian_product(B).is_immutable()
20063
+ True
20064
+ sage: B.cartesian_product(B, immutable=False).is_immutable()
20065
+ False
19762
20066
  """
19763
20067
  self._scream_if_not_simple(allow_loops=True)
19764
20068
  if self._directed and other._directed:
19765
- from sage.graphs.digraph import DiGraph
19766
- G = DiGraph(loops=(self.has_loops() or other.has_loops()))
20069
+ from sage.graphs.digraph import DiGraph as GT
19767
20070
  elif (not self._directed) and (not other._directed):
19768
- from sage.graphs.graph import Graph
19769
- G = Graph(loops=(self.has_loops() or other.has_loops()))
20071
+ from sage.graphs.graph import Graph as GT
19770
20072
  else:
19771
20073
  raise TypeError('the graphs should be both directed or both undirected')
19772
20074
 
19773
- G.add_vertices((u, v) for u in self for v in other)
19774
- for u, w in self.edge_iterator(labels=None):
19775
- for v in other:
19776
- G.add_edge((u, v), (w, v))
19777
- for v, x in other.edge_iterator(labels=None):
19778
- for u in self:
19779
- G.add_edge((u, v), (u, x))
19780
- return G
20075
+ if immutable is None:
20076
+ immutable = self.is_immutable() and other.is_immutable()
20077
+ loops = self.has_loops() or other.has_loops()
20078
+ vertices = ((u, v) for u in self for v in other)
20079
+ from itertools import chain
20080
+ edges = chain((((u, v), (w, v))
20081
+ for u, w in self.edge_iterator(labels=False)
20082
+ for v in other),
20083
+ (((u, v), (u, x))
20084
+ for v, x in other.edge_iterator(labels=False)
20085
+ for u in self))
19781
20086
 
19782
- def tensor_product(self, other):
20087
+ return GT([vertices, edges], format='vertices_and_edges',
20088
+ loops=loops, immutable=immutable)
20089
+
20090
+ def tensor_product(self, other, immutable=None):
19783
20091
  r"""
19784
20092
  Return the tensor product of ``self`` and ``other``.
19785
20093
 
@@ -19792,6 +20100,15 @@ class GenericGraph(GenericGraph_pyx):
19792
20100
  Kronecker product (referring to the Kronecker matrix product). See
19793
20101
  the :wikipedia:`Kronecker_product`.
19794
20102
 
20103
+ INPUT:
20104
+
20105
+ - ``other`` -- a graph or a digraph
20106
+
20107
+ - ``immutable`` -- boolean (default: ``None``); whether to create a
20108
+ mutable/immutable product. ``immutable=None`` (default) means that the
20109
+ graphs and their product will behave the same way. If only one of them
20110
+ is immutable, the product will be mutable.
20111
+
19795
20112
  EXAMPLES::
19796
20113
 
19797
20114
  sage: Z = graphs.CompleteGraph(2)
@@ -19844,28 +20161,53 @@ class GenericGraph(GenericGraph_pyx):
19844
20161
  sage: T = B1.tensor_product(B2)
19845
20162
  sage: T.is_isomorphic(digraphs.DeBruijn(2 * 3, 3))
19846
20163
  True
20164
+
20165
+ Check the behavior of parameter ``immutable``::
20166
+
20167
+ sage: A = Graph([(0, 1)])
20168
+ sage: B = Graph([('a', 'b')], immutable=True)
20169
+ sage: A.tensor_product(A).is_immutable()
20170
+ False
20171
+ sage: A.tensor_product(A, immutable=True).is_immutable()
20172
+ True
20173
+ sage: A.tensor_product(B).is_immutable()
20174
+ False
20175
+ sage: A.tensor_product(B, immutable=True).is_immutable()
20176
+ True
20177
+ sage: B.tensor_product(B).is_immutable()
20178
+ True
20179
+ sage: B.tensor_product(B, immutable=False).is_immutable()
20180
+ False
19847
20181
  """
19848
20182
  self._scream_if_not_simple(allow_loops=True)
19849
20183
  if self._directed and other._directed:
19850
- from sage.graphs.digraph import DiGraph
19851
- G = DiGraph(loops=(self.has_loops() or other.has_loops()))
20184
+ from sage.graphs.digraph import DiGraph as GT
20185
+ edges = (((u, v), (w, x))
20186
+ for u, w in self.edge_iterator(labels=False)
20187
+ for v, x in other.edge_iterator(labels=False))
19852
20188
  elif (not self._directed) and (not other._directed):
19853
- from sage.graphs.graph import Graph
19854
- G = Graph(loops=(self.has_loops() or other.has_loops()))
20189
+ from sage.graphs.graph import Graph as GT
20190
+ from itertools import chain
20191
+ edges = chain((((u, v), (w, x))
20192
+ for u, w in self.edge_iterator(labels=False)
20193
+ for v, x in other.edge_iterator(labels=False)),
20194
+ (((u, x), (w, v))
20195
+ for u, w in self.edge_iterator(labels=False)
20196
+ for v, x in other.edge_iterator(labels=False)))
19855
20197
  else:
19856
20198
  raise TypeError('the graphs should be both directed or both undirected')
19857
- G.add_vertices((u, v) for u in self for v in other)
19858
- for u, w in self.edge_iterator(labels=None):
19859
- for v, x in other.edge_iterator(labels=None):
19860
- G.add_edge((u, v), (w, x))
19861
- if not G._directed:
19862
- G.add_edge((u, x), (w, v))
19863
- return G
20199
+
20200
+ if immutable is None:
20201
+ immutable = self.is_immutable() and other.is_immutable()
20202
+ loops = self.has_loops() or other.has_loops()
20203
+ vertices = ((u, v) for u in self for v in other)
20204
+ return GT([vertices, edges], format='vertices_and_edges',
20205
+ loops=loops, immutable=immutable)
19864
20206
 
19865
20207
  categorical_product = tensor_product
19866
20208
  kronecker_product = tensor_product
19867
20209
 
19868
- def lexicographic_product(self, other):
20210
+ def lexicographic_product(self, other, immutable=None):
19869
20211
  r"""
19870
20212
  Return the lexicographic product of ``self`` and ``other``.
19871
20213
 
@@ -19875,6 +20217,15 @@ class GenericGraph(GenericGraph_pyx):
19875
20217
  * `(u, w)` is an edge of `G`, or
19876
20218
  * `u = w` and `(v, x)` is an edge of `H`.
19877
20219
 
20220
+ INPUT:
20221
+
20222
+ - ``other`` -- a graph or a digraph
20223
+
20224
+ - ``immutable`` -- boolean (default: ``None``); whether to create a
20225
+ mutable/immutable product. ``immutable=None`` (default) means that the
20226
+ graphs and their product will behave the same way. If only one of them
20227
+ is immutable, the product will be mutable.
20228
+
19878
20229
  EXAMPLES::
19879
20230
 
19880
20231
  sage: Z = graphs.CompleteGraph(2)
@@ -19920,27 +20271,48 @@ class GenericGraph(GenericGraph_pyx):
19920
20271
  ((1, 'b'), (2, 'b')), ((2, 'a'), (2, 'b'))]
19921
20272
  sage: T.is_isomorphic(J.lexicographic_product(I))
19922
20273
  False
20274
+
20275
+ Check the behavior of parameter ``immutable``::
20276
+
20277
+ sage: A = Graph([(0, 1)])
20278
+ sage: B = Graph([('a', 'b')], immutable=True)
20279
+ sage: A.lexicographic_product(A).is_immutable()
20280
+ False
20281
+ sage: A.lexicographic_product(A, immutable=True).is_immutable()
20282
+ True
20283
+ sage: A.lexicographic_product(B).is_immutable()
20284
+ False
20285
+ sage: A.lexicographic_product(B, immutable=True).is_immutable()
20286
+ True
20287
+ sage: B.lexicographic_product(B).is_immutable()
20288
+ True
20289
+ sage: B.lexicographic_product(B, immutable=False).is_immutable()
20290
+ False
19923
20291
  """
19924
20292
  self._scream_if_not_simple(allow_loops=True)
19925
20293
  if self._directed and other._directed:
19926
- from sage.graphs.digraph import DiGraph
19927
- G = DiGraph(loops=(self.has_loops() or other.has_loops()))
20294
+ from sage.graphs.digraph import DiGraph as GT
19928
20295
  elif (not self._directed) and (not other._directed):
19929
- from sage.graphs.graph import Graph
19930
- G = Graph(loops=(self.has_loops() or other.has_loops()))
20296
+ from sage.graphs.graph import Graph as GT
19931
20297
  else:
19932
20298
  raise TypeError('the graphs should be both directed or both undirected')
19933
- G.add_vertices((u, v) for u in self for v in other)
19934
- for u, w in self.edge_iterator(labels=None):
19935
- for v in other:
19936
- for x in other:
19937
- G.add_edge((u, v), (w, x))
19938
- for u in self:
19939
- for v, x in other.edge_iterator(labels=None):
19940
- G.add_edge((u, v), (u, x))
19941
- return G
19942
20299
 
19943
- def strong_product(self, other):
20300
+ if immutable is None:
20301
+ immutable = self.is_immutable() and other.is_immutable()
20302
+ loops = self.has_loops() or other.has_loops()
20303
+ vertices = ((u, v) for u in self for v in other)
20304
+ from itertools import chain
20305
+ edges = chain((((u, v), (w, x))
20306
+ for u, w in self.edge_iterator(labels=False)
20307
+ for v in other
20308
+ for x in other),
20309
+ (((u, v), (u, x))
20310
+ for u in self
20311
+ for v, x in other.edge_iterator(labels=False)))
20312
+ return GT([vertices, edges], format='vertices_and_edges',
20313
+ loops=loops, immutable=immutable)
20314
+
20315
+ def strong_product(self, other, immutable=None):
19944
20316
  r"""
19945
20317
  Return the strong product of ``self`` and ``other``.
19946
20318
 
@@ -19955,6 +20327,15 @@ class GenericGraph(GenericGraph_pyx):
19955
20327
  In other words, the edges of the strong product is the union of the
19956
20328
  edges of the tensor and Cartesian products.
19957
20329
 
20330
+ INPUT:
20331
+
20332
+ - ``other`` -- a graph or a digraph
20333
+
20334
+ - ``immutable`` -- boolean (default: ``None``); whether to create a
20335
+ mutable/immutable product. ``immutable=None`` (default) means that the
20336
+ graphs and their product will behave the same way. If only one of them
20337
+ is immutable, the product will be mutable.
20338
+
19958
20339
  EXAMPLES::
19959
20340
 
19960
20341
  sage: Z = graphs.CompleteGraph(2)
@@ -20002,31 +20383,57 @@ class GenericGraph(GenericGraph_pyx):
20002
20383
  sage: expected = gm * hn + hm * gn + 2 * gm * hm
20003
20384
  sage: product_size == expected
20004
20385
  True
20386
+
20387
+ Check the behavior of parameter ``immutable``::
20388
+
20389
+ sage: A = Graph([(0, 1)])
20390
+ sage: B = Graph([('a', 'b')], immutable=True)
20391
+ sage: A.strong_product(A).is_immutable()
20392
+ False
20393
+ sage: A.strong_product(A, immutable=True).is_immutable()
20394
+ True
20395
+ sage: A.strong_product(B).is_immutable()
20396
+ False
20397
+ sage: A.strong_product(B, immutable=True).is_immutable()
20398
+ True
20399
+ sage: B.strong_product(B).is_immutable()
20400
+ True
20401
+ sage: B.strong_product(B, immutable=False).is_immutable()
20402
+ False
20005
20403
  """
20006
20404
  self._scream_if_not_simple(allow_loops=True)
20007
20405
  if self._directed and other._directed:
20008
- from sage.graphs.digraph import DiGraph
20009
- G = DiGraph(loops=(self.has_loops() or other.has_loops()))
20406
+ from sage.graphs.digraph import DiGraph as GT
20010
20407
  elif (not self._directed) and (not other._directed):
20011
- from sage.graphs.graph import Graph
20012
- G = Graph(loops=(self.has_loops() or other.has_loops()))
20408
+ from sage.graphs.graph import Graph as GT
20013
20409
  else:
20014
20410
  raise TypeError('the graphs should be both directed or both undirected')
20015
20411
 
20016
- G.add_vertices((u, v) for u in self for v in other)
20017
- for u, w in self.edge_iterator(labels=None):
20018
- for v in other:
20019
- G.add_edge((u, v), (w, v))
20020
- for v, x in other.edge_iterator(labels=None):
20021
- G.add_edge((u, v), (w, x))
20022
- if not self._directed:
20023
- G.add_edge((w, v), (u, x))
20024
- for v, x in other.edge_iterator(labels=None):
20025
- for u in self:
20026
- G.add_edge((u, v), (u, x))
20027
- return G
20412
+ if immutable is None:
20413
+ immutable = self.is_immutable() and other.is_immutable()
20414
+ loops = self.has_loops() or other.has_loops()
20415
+ vertices = ((u, v) for u in self for v in other)
20416
+
20417
+ edges_1 = (((u, v), (w, v))
20418
+ for u, w in self.edge_iterator(labels=False) for v in other)
20419
+ edges_2 = (((u, v), (u, x))
20420
+ for v, x in other.edge_iterator(labels=False) for u in self)
20421
+ edges_3 = (((u, v), (w, x))
20422
+ for u, w in self.edge_iterator(labels=False)
20423
+ for v, x in other.edge_iterator(labels=False))
20424
+ if self._directed:
20425
+ edges_4 = ()
20426
+ else:
20427
+ edges_4 = (((w, v), (u, x))
20428
+ for u, w in self.edge_iterator(labels=False)
20429
+ for v, x in other.edge_iterator(labels=False))
20430
+
20431
+ from itertools import chain
20432
+ edges = chain(edges_1, edges_2, edges_3, edges_4)
20433
+ return GT([vertices, edges], format='vertices_and_edges',
20434
+ loops=loops, immutable=immutable)
20028
20435
 
20029
- def disjunctive_product(self, other):
20436
+ def disjunctive_product(self, other, immutable=None):
20030
20437
  r"""
20031
20438
  Return the disjunctive product of ``self`` and ``other``.
20032
20439
 
@@ -20036,6 +20443,15 @@ class GenericGraph(GenericGraph_pyx):
20036
20443
  * `(u, w)` is an edge of `G`, or
20037
20444
  * `(v, x)` is an edge of `H`.
20038
20445
 
20446
+ INPUT:
20447
+
20448
+ - ``other`` -- a graph or a digraph
20449
+
20450
+ - ``immutable`` -- boolean (default: ``None``); whether to create a
20451
+ mutable/immutable product. ``immutable=None`` (default) means that the
20452
+ graphs and their product will behave the same way. If only one of them
20453
+ is immutable, the product will be mutable.
20454
+
20039
20455
  EXAMPLES::
20040
20456
 
20041
20457
  sage: Z = graphs.CompleteGraph(2)
@@ -20081,27 +20497,48 @@ class GenericGraph(GenericGraph_pyx):
20081
20497
  ((2, 'a'), (0, 'b')), ((2, 'a'), (1, 'b')), ((2, 'a'), (2, 'b'))]
20082
20498
  sage: T.is_isomorphic(J.disjunctive_product(I))
20083
20499
  True
20500
+
20501
+ Check the behavior of parameter ``immutable``::
20502
+
20503
+ sage: A = Graph([(0, 1)])
20504
+ sage: B = Graph([('a', 'b')], immutable=True)
20505
+ sage: A.disjunctive_product(A).is_immutable()
20506
+ False
20507
+ sage: A.disjunctive_product(A, immutable=True).is_immutable()
20508
+ True
20509
+ sage: A.disjunctive_product(B).is_immutable()
20510
+ False
20511
+ sage: A.disjunctive_product(B, immutable=True).is_immutable()
20512
+ True
20513
+ sage: B.disjunctive_product(B).is_immutable()
20514
+ True
20515
+ sage: B.disjunctive_product(B, immutable=False).is_immutable()
20516
+ False
20084
20517
  """
20085
20518
  self._scream_if_not_simple(allow_loops=True)
20086
20519
  if self._directed and other._directed:
20087
- from sage.graphs.digraph import DiGraph
20088
- G = DiGraph(loops=(self.has_loops() or other.has_loops()))
20520
+ from sage.graphs.digraph import DiGraph as GT
20089
20521
  elif (not self._directed) and (not other._directed):
20090
- from sage.graphs.graph import Graph
20091
- G = Graph(loops=(self.has_loops() or other.has_loops()))
20522
+ from sage.graphs.graph import Graph as GT
20092
20523
  else:
20093
20524
  raise TypeError('the graphs should be both directed or both undirected')
20094
20525
 
20095
- G.add_vertices((u, v) for u in self for v in other)
20096
- for u, w in self.edge_iterator(labels=None):
20097
- for v in other:
20098
- for x in other:
20099
- G.add_edge((u, v), (w, x))
20100
- for v, x in other.edge_iterator(labels=None):
20101
- for u in self:
20102
- for w in self:
20103
- G.add_edge((u, v), (w, x))
20104
- return G
20526
+ if immutable is None:
20527
+ immutable = self.is_immutable() and other.is_immutable()
20528
+ loops = self.has_loops() or other.has_loops()
20529
+ vertices = ((u, v) for u in self for v in other)
20530
+ edges_1 = (((u, v), (w, x))
20531
+ for u, w in self.edge_iterator(labels=False)
20532
+ for v in other
20533
+ for x in other)
20534
+ edges_2 = (((u, v), (w, x))
20535
+ for v, x in other.edge_iterator(labels=False)
20536
+ for u in self
20537
+ for w in self)
20538
+ from itertools import chain
20539
+ edges = chain(edges_1, edges_2)
20540
+ return GT([vertices, edges], format='vertices_and_edges',
20541
+ loops=loops, immutable=immutable)
20105
20542
 
20106
20543
  def transitive_closure(self, loops=True):
20107
20544
  r"""
@@ -23032,68 +23469,55 @@ class GenericGraph(GenericGraph_pyx):
23032
23469
 
23033
23470
  sage: P = graphs.PetersenGraph()
23034
23471
  sage: P.eigenvectors() # needs sage.modules sage.rings.number_field
23035
- [(3, [
23036
- (1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
23037
- ], 1), (-2, [
23038
- (1, 0, 0, 0, -1, -1, -1, 0, 1, 1),
23039
- (0, 1, 0, 0, -1, 0, -2, -1, 1, 2),
23040
- (0, 0, 1, 0, -1, 1, -1, -2, 0, 2),
23041
- (0, 0, 0, 1, -1, 1, 0, -1, -1, 1)
23042
- ], 4), (1, [
23043
- (1, 0, 0, 0, 0, 1, -1, 0, 0, -1),
23044
- (0, 1, 0, 0, 0, -1, 1, -1, 0, 0),
23045
- (0, 0, 1, 0, 0, 0, -1, 1, -1, 0),
23046
- (0, 0, 0, 1, 0, 0, 0, -1, 1, -1),
23047
- (0, 0, 0, 0, 1, -1, 0, 0, -1, 1)
23048
- ], 5)]
23472
+ [(3, [(1, 1, 1, 1, 1, 1, 1, 1, 1, 1)], 1),
23473
+ (-2,
23474
+ [(1, 0, 0, 0, -1, -1, -1, 0, 1, 1),
23475
+ (0, 1, 0, 0, -1, 0, -2, -1, 1, 2),
23476
+ (0, 0, 1, 0, -1, 1, -1, -2, 0, 2),
23477
+ (0, 0, 0, 1, -1, 1, 0, -1, -1, 1)],
23478
+ 4),
23479
+ (1,
23480
+ [(1, 0, 0, 0, 0, 1, -1, 0, 0, -1),
23481
+ (0, 1, 0, 0, 0, -1, 1, -1, 0, 0),
23482
+ (0, 0, 1, 0, 0, 0, -1, 1, -1, 0),
23483
+ (0, 0, 0, 1, 0, 0, 0, -1, 1, -1),
23484
+ (0, 0, 0, 0, 1, -1, 0, 0, -1, 1)],
23485
+ 5)]
23049
23486
 
23050
23487
  Eigenspaces for the Laplacian should be identical since the Petersen
23051
23488
  graph is regular. However, since the output also contains the
23052
23489
  eigenvalues, the two outputs are slightly different::
23053
23490
 
23054
23491
  sage: P.eigenvectors(laplacian=True) # needs sage.modules sage.rings.number_field
23055
- [(0, [
23056
- (1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
23057
- ], 1), (5, [
23058
- (1, 0, 0, 0, -1, -1, -1, 0, 1, 1),
23059
- (0, 1, 0, 0, -1, 0, -2, -1, 1, 2),
23060
- (0, 0, 1, 0, -1, 1, -1, -2, 0, 2),
23061
- (0, 0, 0, 1, -1, 1, 0, -1, -1, 1)
23062
- ], 4), (2, [
23063
- (1, 0, 0, 0, 0, 1, -1, 0, 0, -1),
23064
- (0, 1, 0, 0, 0, -1, 1, -1, 0, 0),
23065
- (0, 0, 1, 0, 0, 0, -1, 1, -1, 0),
23066
- (0, 0, 0, 1, 0, 0, 0, -1, 1, -1),
23067
- (0, 0, 0, 0, 1, -1, 0, 0, -1, 1)
23068
- ], 5)]
23492
+ [(0, [(1, 1, 1, 1, 1, 1, 1, 1, 1, 1)], 1),
23493
+ (5,
23494
+ [(1, 0, 0, 0, -1, -1, -1, 0, 1, 1),
23495
+ (0, 1, 0, 0, -1, 0, -2, -1, 1, 2),
23496
+ (0, 0, 1, 0, -1, 1, -1, -2, 0, 2),
23497
+ (0, 0, 0, 1, -1, 1, 0, -1, -1, 1)],
23498
+ 4),
23499
+ (2,
23500
+ [(1, 0, 0, 0, 0, 1, -1, 0, 0, -1),
23501
+ (0, 1, 0, 0, 0, -1, 1, -1, 0, 0),
23502
+ (0, 0, 1, 0, 0, 0, -1, 1, -1, 0),
23503
+ (0, 0, 0, 1, 0, 0, 0, -1, 1, -1),
23504
+ (0, 0, 0, 0, 1, -1, 0, 0, -1, 1)],
23505
+ 5)]
23069
23506
 
23070
23507
  ::
23071
23508
 
23072
23509
  sage: C = graphs.CycleGraph(8)
23073
23510
  sage: C.eigenvectors() # needs sage.modules sage.rings.number_field
23074
- [(2,
23075
- [
23076
- (1, 1, 1, 1, 1, 1, 1, 1)
23077
- ],
23078
- 1),
23079
- (-2,
23080
- [
23081
- (1, -1, 1, -1, 1, -1, 1, -1)
23082
- ],
23083
- 1),
23084
- (0,
23085
- [
23086
- (1, 0, -1, 0, 1, 0, -1, 0),
23087
- (0, 1, 0, -1, 0, 1, 0, -1)
23088
- ],
23511
+ [(2, [(1, 1, 1, 1, 1, 1, 1, 1)], 1),
23512
+ (-2, [(1, -1, 1, -1, 1, -1, 1, -1)], 1),
23513
+ (0, [(1, 0, -1, 0, 1, 0, -1, 0), (0, 1, 0, -1, 0, 1, 0, -1)], 2),
23514
+ (-1.414213562373095?,
23515
+ [(1, 0, -1, 1.414213562373095?, -1, 0, 1, -1.414213562373095?),
23516
+ (0, 1, -1.414213562373095?, 1, 0, -1, 1.414213562373095?, -1)],
23089
23517
  2),
23090
- (-1.4142135623...,
23091
- [(1, 0, -1, 1.4142135623..., -1, 0, 1, -1.4142135623...),
23092
- (0, 1, -1.4142135623..., 1, 0, -1, 1.4142135623..., -1)],
23093
- 2),
23094
- (1.4142135623...,
23095
- [(1, 0, -1, -1.4142135623..., -1, 0, 1, 1.4142135623...),
23096
- (0, 1, 1.4142135623..., 1, 0, -1, -1.4142135623..., -1)],
23518
+ (1.414213562373095?,
23519
+ [(1, 0, -1, -1.414213562373095?, -1, 0, 1, 1.414213562373095?),
23520
+ (0, 1, 1.414213562373095?, 1, 0, -1, -1.414213562373095?, -1)],
23097
23521
  2)]
23098
23522
 
23099
23523
  A digraph may have complex eigenvalues. Previously, the complex parts of
@@ -23101,16 +23525,12 @@ class GenericGraph(GenericGraph_pyx):
23101
23525
 
23102
23526
  sage: T = DiGraph({0:[1], 1:[2], 2:[0]})
23103
23527
  sage: T.eigenvectors() # needs sage.modules sage.rings.number_field
23104
- [(1,
23105
- [
23106
- (1, 1, 1)
23107
- ],
23528
+ [(1, [(1, 1, 1)], 1),
23529
+ (-0.50000000000000000? - 0.866025403784439?*I,
23530
+ [(1, -0.50000000000000000? - 0.866025403784439?*I, -0.50000000000000000? + 0.866025403784439?*I)],
23108
23531
  1),
23109
- (-0.5000000000... - 0.8660254037...*I,
23110
- [(1, -0.5000000000... - 0.8660254037...*I, -0.5000000000... + 0.8660254037...*I)],
23111
- 1),
23112
- (-0.5000000000... + 0.8660254037...*I,
23113
- [(1, -0.5000000000... + 0.8660254037...*I, -0.5000000000... - 0.8660254037...*I)],
23532
+ (-0.50000000000000000? + 0.866025403784439?*I,
23533
+ [(1, -0.50000000000000000? + 0.866025403784439?*I, -0.50000000000000000? - 0.866025403784439?*I)],
23114
23534
  1)]
23115
23535
  """
23116
23536
  if laplacian:
@@ -23142,48 +23562,50 @@ class GenericGraph(GenericGraph_pyx):
23142
23562
 
23143
23563
  sage: P = graphs.PetersenGraph()
23144
23564
  sage: P.eigenspaces() # needs sage.modules sage.rings.number_field
23145
- [
23146
- (3, Vector space of degree 10 and dimension 1 over Rational Field
23147
- User basis matrix:
23148
- [1 1 1 1 1 1 1 1 1 1]),
23149
- (-2, Vector space of degree 10 and dimension 4 over Rational Field
23150
- User basis matrix:
23151
- [ 1 0 0 0 -1 -1 -1 0 1 1]
23152
- [ 0 1 0 0 -1 0 -2 -1 1 2]
23153
- [ 0 0 1 0 -1 1 -1 -2 0 2]
23154
- [ 0 0 0 1 -1 1 0 -1 -1 1]),
23155
- (1, Vector space of degree 10 and dimension 5 over Rational Field
23156
- User basis matrix:
23157
- [ 1 0 0 0 0 1 -1 0 0 -1]
23158
- [ 0 1 0 0 0 -1 1 -1 0 0]
23159
- [ 0 0 1 0 0 0 -1 1 -1 0]
23160
- [ 0 0 0 1 0 0 0 -1 1 -1]
23161
- [ 0 0 0 0 1 -1 0 0 -1 1])
23162
- ]
23565
+ [(3,
23566
+ Vector space of degree 10 and dimension 1 over Rational Field
23567
+ User basis matrix:
23568
+ [1 1 1 1 1 1 1 1 1 1]),
23569
+ (-2,
23570
+ Vector space of degree 10 and dimension 4 over Rational Field
23571
+ User basis matrix:
23572
+ [ 1 0 0 0 -1 -1 -1 0 1 1]
23573
+ [ 0 1 0 0 -1 0 -2 -1 1 2]
23574
+ [ 0 0 1 0 -1 1 -1 -2 0 2]
23575
+ [ 0 0 0 1 -1 1 0 -1 -1 1]),
23576
+ (1,
23577
+ Vector space of degree 10 and dimension 5 over Rational Field
23578
+ User basis matrix:
23579
+ [ 1 0 0 0 0 1 -1 0 0 -1]
23580
+ [ 0 1 0 0 0 -1 1 -1 0 0]
23581
+ [ 0 0 1 0 0 0 -1 1 -1 0]
23582
+ [ 0 0 0 1 0 0 0 -1 1 -1]
23583
+ [ 0 0 0 0 1 -1 0 0 -1 1])]
23163
23584
 
23164
23585
  Eigenspaces for the Laplacian should be identical since the Petersen
23165
23586
  graph is regular. However, since the output also contains the
23166
23587
  eigenvalues, the two outputs are slightly different::
23167
23588
 
23168
23589
  sage: P.eigenspaces(laplacian=True) # needs sage.modules sage.rings.number_field
23169
- [
23170
- (0, Vector space of degree 10 and dimension 1 over Rational Field
23171
- User basis matrix:
23172
- [1 1 1 1 1 1 1 1 1 1]),
23173
- (5, Vector space of degree 10 and dimension 4 over Rational Field
23174
- User basis matrix:
23175
- [ 1 0 0 0 -1 -1 -1 0 1 1]
23176
- [ 0 1 0 0 -1 0 -2 -1 1 2]
23177
- [ 0 0 1 0 -1 1 -1 -2 0 2]
23178
- [ 0 0 0 1 -1 1 0 -1 -1 1]),
23179
- (2, Vector space of degree 10 and dimension 5 over Rational Field
23180
- User basis matrix:
23181
- [ 1 0 0 0 0 1 -1 0 0 -1]
23182
- [ 0 1 0 0 0 -1 1 -1 0 0]
23183
- [ 0 0 1 0 0 0 -1 1 -1 0]
23184
- [ 0 0 0 1 0 0 0 -1 1 -1]
23185
- [ 0 0 0 0 1 -1 0 0 -1 1])
23186
- ]
23590
+ [(0,
23591
+ Vector space of degree 10 and dimension 1 over Rational Field
23592
+ User basis matrix:
23593
+ [1 1 1 1 1 1 1 1 1 1]),
23594
+ (5,
23595
+ Vector space of degree 10 and dimension 4 over Rational Field
23596
+ User basis matrix:
23597
+ [ 1 0 0 0 -1 -1 -1 0 1 1]
23598
+ [ 0 1 0 0 -1 0 -2 -1 1 2]
23599
+ [ 0 0 1 0 -1 1 -1 -2 0 2]
23600
+ [ 0 0 0 1 -1 1 0 -1 -1 1]),
23601
+ (2,
23602
+ Vector space of degree 10 and dimension 5 over Rational Field
23603
+ User basis matrix:
23604
+ [ 1 0 0 0 0 1 -1 0 0 -1]
23605
+ [ 0 1 0 0 0 -1 1 -1 0 0]
23606
+ [ 0 0 1 0 0 0 -1 1 -1 0]
23607
+ [ 0 0 0 1 0 0 0 -1 1 -1]
23608
+ [ 0 0 0 0 1 -1 0 0 -1 1])]
23187
23609
 
23188
23610
  Notice how one eigenspace below is described with a square root of 2.
23189
23611
  For the two possible values (positive and negative) there is a
@@ -23191,38 +23613,38 @@ class GenericGraph(GenericGraph_pyx):
23191
23613
 
23192
23614
  sage: C = graphs.CycleGraph(8)
23193
23615
  sage: C.eigenspaces() # needs sage.modules sage.rings.number_field
23194
- [
23195
- (2, Vector space of degree 8 and dimension 1 over Rational Field
23196
- User basis matrix:
23197
- [1 1 1 1 1 1 1 1]),
23198
- (-2, Vector space of degree 8 and dimension 1 over Rational Field
23199
- User basis matrix:
23200
- [ 1 -1 1 -1 1 -1 1 -1]),
23201
- (0, Vector space of degree 8 and dimension 2 over Rational Field
23202
- User basis matrix:
23203
- [ 1 0 -1 0 1 0 -1 0]
23204
- [ 0 1 0 -1 0 1 0 -1]),
23205
- (a3, Vector space of degree 8 and dimension 2 over
23206
- Number Field in a3 with defining polynomial x^2 - 2
23207
- User basis matrix:
23208
- [ 1 0 -1 -a3 -1 0 1 a3]
23209
- [ 0 1 a3 1 0 -1 -a3 -1])
23210
- ]
23616
+ [(2,
23617
+ Vector space of degree 8 and dimension 1 over Rational Field
23618
+ User basis matrix:
23619
+ [1 1 1 1 1 1 1 1]),
23620
+ (-2,
23621
+ Vector space of degree 8 and dimension 1 over Rational Field
23622
+ User basis matrix:
23623
+ [ 1 -1 1 -1 1 -1 1 -1]),
23624
+ (0,
23625
+ Vector space of degree 8 and dimension 2 over Rational Field
23626
+ User basis matrix:
23627
+ [ 1 0 -1 0 1 0 -1 0]
23628
+ [ 0 1 0 -1 0 1 0 -1]),
23629
+ (a3,
23630
+ Vector space of degree 8 and dimension 2 over Number Field in a3 with defining polynomial x^2 - 2
23631
+ User basis matrix:
23632
+ [ 1 0 -1 -a3 -1 0 1 a3]
23633
+ [ 0 1 a3 1 0 -1 -a3 -1])]
23211
23634
 
23212
23635
  A digraph may have complex eigenvalues and eigenvectors. For a 3-cycle,
23213
23636
  we have::
23214
23637
 
23215
23638
  sage: T = DiGraph({0: [1], 1: [2], 2: [0]})
23216
23639
  sage: T.eigenspaces() # needs sage.modules sage.rings.number_field
23217
- [
23218
- (1, Vector space of degree 3 and dimension 1 over Rational Field
23219
- User basis matrix:
23220
- [1 1 1]),
23221
- (a1, Vector space of degree 3 and dimension 1 over Number Field in a1
23222
- with defining polynomial x^2 + x + 1
23223
- User basis matrix:
23224
- [ 1 a1 -a1 - 1])
23225
- ]
23640
+ [(1,
23641
+ Vector space of degree 3 and dimension 1 over Rational Field
23642
+ User basis matrix:
23643
+ [1 1 1]),
23644
+ (a1,
23645
+ Vector space of degree 3 and dimension 1 over Number Field in a1 with defining polynomial x^2 + x + 1
23646
+ User basis matrix:
23647
+ [ 1 a1 -a1 - 1])]
23226
23648
  """
23227
23649
  if laplacian:
23228
23650
  M = self.kirchhoff_matrix(vertices=list(self))