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.
- {passagemath_graphs-10.5.10.dist-info → passagemath_graphs-10.5.43.dist-info}/METADATA +126 -30
- passagemath_graphs-10.5.43.dist-info/RECORD +256 -0
- {passagemath_graphs-10.5.10.dist-info → passagemath_graphs-10.5.43.dist-info}/WHEEL +2 -1
- passagemath_graphs.dylibs/libgmp.10.dylib +0 -0
- sage/all__sagemath_graphs.py +5 -0
- sage/combinat/abstract_tree.py +1 -1
- sage/combinat/binary_tree.py +1 -1
- sage/combinat/cluster_algebra_quiver/all.py +1 -1
- sage/combinat/cluster_algebra_quiver/cluster_seed.py +28 -24
- sage/combinat/cluster_algebra_quiver/interact.py +4 -0
- sage/combinat/designs/MOLS_handbook_data.py +5 -5
- sage/combinat/designs/bibd.py +10 -9
- sage/combinat/designs/covering_array.py +3 -3
- sage/combinat/designs/covering_design.py +2 -1
- sage/combinat/designs/database.py +11 -10
- sage/combinat/designs/designs_pyx.cpython-39-darwin.so +0 -0
- sage/combinat/designs/designs_pyx.pyx +13 -45
- sage/combinat/designs/difference_family.py +6 -6
- sage/combinat/designs/difference_matrices.py +1 -1
- sage/combinat/designs/evenly_distributed_sets.cpython-39-darwin.so +0 -0
- sage/combinat/designs/evenly_distributed_sets.pyx +15 -22
- sage/combinat/designs/ext_rep.py +9 -14
- sage/combinat/designs/gen_quadrangles_with_spread.cpython-39-darwin.so +0 -0
- sage/combinat/designs/gen_quadrangles_with_spread.pyx +1 -1
- sage/combinat/designs/group_divisible_designs.py +1 -1
- sage/combinat/designs/incidence_structures.py +8 -8
- sage/combinat/designs/latin_squares.py +1 -1
- sage/combinat/designs/orthogonal_arrays_build_recursive.py +8 -7
- sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-39-darwin.so +0 -0
- sage/combinat/designs/resolvable_bibd.py +1 -1
- sage/combinat/designs/steiner_quadruple_systems.py +1 -1
- sage/combinat/designs/subhypergraph_search.cpython-39-darwin.so +0 -0
- sage/combinat/designs/subhypergraph_search.pyx +9 -9
- sage/combinat/finite_state_machine_generators.py +2 -2
- sage/combinat/graph_path.py +3 -3
- sage/combinat/interval_posets.py +10 -10
- sage/combinat/ordered_tree.py +1 -1
- sage/combinat/posets/cartesian_product.py +1 -1
- sage/combinat/posets/d_complete.py +1 -1
- sage/combinat/posets/forest.py +1 -1
- sage/combinat/posets/hasse_cython.cpython-39-darwin.so +0 -0
- sage/combinat/posets/hasse_diagram.py +8 -6
- sage/combinat/posets/incidence_algebras.py +8 -8
- sage/combinat/posets/lattices.py +28 -4
- sage/combinat/posets/linear_extension_iterator.cpython-39-darwin.so +0 -0
- sage/combinat/posets/linear_extension_iterator.pyx +2 -0
- sage/combinat/posets/linear_extensions.py +7 -16
- sage/combinat/posets/moebius_algebra.py +1 -1
- sage/combinat/posets/poset_examples.py +1 -1
- sage/combinat/posets/posets.py +54 -56
- sage/combinat/rooted_tree.py +3 -3
- sage/combinat/tamari_lattices.py +1 -1
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/graphs/asteroidal_triples.cpython-39-darwin.so +0 -0
- sage/graphs/base/boost_graph.cpython-39-darwin.so +0 -0
- sage/graphs/base/boost_graph.pxd +1 -1
- sage/graphs/base/boost_graph.pyx +1 -1
- sage/graphs/base/c_graph.cpython-39-darwin.so +0 -0
- sage/graphs/base/c_graph.pxd +4 -4
- sage/graphs/base/c_graph.pyx +270 -184
- sage/graphs/base/dense_graph.cpython-39-darwin.so +0 -0
- sage/graphs/base/graph_backends.cpython-39-darwin.so +0 -0
- sage/graphs/base/sparse_graph.cpython-39-darwin.so +0 -0
- sage/graphs/base/static_dense_graph.cpython-39-darwin.so +0 -0
- sage/graphs/base/static_sparse_backend.cpython-39-darwin.so +0 -0
- sage/graphs/base/static_sparse_backend.pyx +93 -6
- sage/graphs/base/static_sparse_graph.cpython-39-darwin.so +0 -0
- sage/graphs/base/static_sparse_graph.pyx +1 -1
- sage/graphs/bipartite_graph.py +0 -1
- sage/graphs/centrality.cpython-39-darwin.so +0 -0
- sage/graphs/centrality.pyx +0 -0
- sage/graphs/comparability.cpython-39-darwin.so +0 -0
- sage/graphs/comparability.pyx +172 -138
- sage/graphs/connectivity.cpython-39-darwin.so +0 -0
- sage/graphs/connectivity.pyx +194 -18
- sage/graphs/convexity_properties.cpython-39-darwin.so +0 -0
- sage/graphs/digraph_generators.py +118 -74
- sage/graphs/distances_all_pairs.cpython-39-darwin.so +0 -0
- sage/graphs/distances_all_pairs.pyx +145 -27
- sage/graphs/edge_connectivity.cpython-39-darwin.so +0 -0
- sage/graphs/generators/basic.py +471 -130
- sage/graphs/generators/distance_regular.cpython-39-darwin.so +0 -0
- sage/graphs/generators/distance_regular.pyx +12 -12
- sage/graphs/generators/families.py +2 -2
- sage/graphs/generators/random.py +8 -13
- sage/graphs/generators/smallgraphs.py +12 -11
- sage/graphs/generic_graph.py +687 -265
- sage/graphs/generic_graph_pyx.cpython-39-darwin.so +0 -0
- sage/graphs/genus.cpython-39-darwin.so +0 -0
- sage/graphs/graph.py +12 -46
- sage/graphs/graph_coloring.cpython-39-darwin.so +0 -0
- sage/graphs/graph_database.py +1 -1
- sage/graphs/graph_decompositions/bandwidth.cpython-39-darwin.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.cpython-39-darwin.so +0 -0
- sage/graphs/graph_decompositions/cutwidth.cpython-39-darwin.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.cpython-39-darwin.so +0 -0
- sage/graphs/graph_decompositions/graph_products.cpython-39-darwin.so +0 -0
- sage/graphs/graph_decompositions/modular_decomposition.cpython-39-darwin.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.cpython-39-darwin.so +0 -0
- sage/graphs/graph_decompositions/tree_decomposition.cpython-39-darwin.so +0 -0
- sage/graphs/graph_decompositions/vertex_separation.cpython-39-darwin.so +0 -0
- sage/graphs/graph_generators.py +110 -55
- sage/graphs/graph_generators_pyx.cpython-39-darwin.so +0 -0
- sage/graphs/graph_latex.py +1 -1
- sage/graphs/graph_list.py +2 -3
- sage/graphs/graph_plot.py +225 -30
- sage/graphs/hyperbolicity.cpython-39-darwin.so +0 -0
- sage/graphs/independent_sets.cpython-39-darwin.so +0 -0
- sage/graphs/isgci.py +3 -8
- sage/graphs/isoperimetric_inequalities.cpython-39-darwin.so +0 -0
- sage/graphs/line_graph.cpython-39-darwin.so +0 -0
- sage/graphs/matching.py +14 -25
- sage/graphs/matching_covered_graph.py +871 -60
- sage/graphs/orientations.py +190 -134
- sage/graphs/path_enumeration.cpython-39-darwin.so +0 -0
- sage/graphs/path_enumeration.pyx +25 -25
- sage/graphs/spanning_tree.cpython-39-darwin.so +0 -0
- sage/graphs/strongly_regular_db.cpython-39-darwin.so +0 -0
- sage/graphs/strongly_regular_db.pyx +54 -52
- sage/graphs/traversals.cpython-39-darwin.so +0 -0
- sage/graphs/traversals.pyx +114 -46
- sage/graphs/trees.cpython-39-darwin.so +0 -0
- sage/graphs/views.cpython-39-darwin.so +0 -0
- sage/graphs/weakly_chordal.cpython-39-darwin.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-39-darwin.so +0 -0
- sage/knots/free_knotinfo_monoid.py +2 -3
- sage/knots/knot.py +1 -1
- sage/knots/knotinfo.py +4 -4
- sage/knots/link.py +58 -57
- sage/sandpiles/sandpile.py +2 -3
- sage/topology/cell_complex.py +1 -1
- sage/topology/cubical_complex.py +7 -7
- sage/topology/delta_complex.py +4 -4
- sage/topology/simplicial_complex.py +7 -22
- passagemath_graphs-10.5.10.dist-info/RECORD +0 -251
- {passagemath_graphs-10.5.10.dist-info → passagemath_graphs-10.5.43.dist-info}/top_level.txt +0 -0
sage/graphs/generic_graph.py
CHANGED
@@ -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
|
-
|
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
|
-
|
791
|
-
|
792
|
-
|
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
|
-
|
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
|
-
[[
|
5379
|
-
[
|
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
|
-
[[(
|
5385
|
-
(
|
5386
|
-
(
|
5387
|
-
|
5388
|
-
|
5389
|
-
|
5390
|
-
|
5391
|
-
|
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
|
-
[[
|
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
|
-
[[
|
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
|
-
[[
|
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
|
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
|
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,
|
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
|
-
|
8751
|
-
|
8752
|
-
|
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
|
-
|
8889
|
-
|
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
|
-
|
8893
|
-
|
8894
|
-
|
8895
|
-
|
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',
|
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
|
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,
|
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
|
-
|
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(
|
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
|
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
|
-
|
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
|
-
|
19599
|
-
|
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
|
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
|
-
|
19695
|
-
|
19696
|
-
|
19697
|
-
|
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
|
-
|
19774
|
-
|
19775
|
-
|
19776
|
-
|
19777
|
-
|
19778
|
-
|
19779
|
-
|
19780
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
19858
|
-
|
19859
|
-
|
19860
|
-
|
19861
|
-
|
19862
|
-
|
19863
|
-
|
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
|
-
|
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
|
-
|
20017
|
-
|
20018
|
-
|
20019
|
-
|
20020
|
-
|
20021
|
-
|
20022
|
-
|
20023
|
-
|
20024
|
-
|
20025
|
-
|
20026
|
-
|
20027
|
-
|
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
|
-
|
20096
|
-
|
20097
|
-
|
20098
|
-
|
20099
|
-
|
20100
|
-
|
20101
|
-
|
20102
|
-
|
20103
|
-
|
20104
|
-
|
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
|
-
|
23037
|
-
|
23038
|
-
|
23039
|
-
|
23040
|
-
|
23041
|
-
|
23042
|
-
|
23043
|
-
|
23044
|
-
|
23045
|
-
|
23046
|
-
|
23047
|
-
|
23048
|
-
|
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
|
-
|
23057
|
-
|
23058
|
-
|
23059
|
-
|
23060
|
-
|
23061
|
-
|
23062
|
-
|
23063
|
-
|
23064
|
-
|
23065
|
-
|
23066
|
-
|
23067
|
-
|
23068
|
-
|
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
|
-
|
23077
|
-
|
23078
|
-
1),
|
23079
|
-
|
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
|
-
(
|
23091
|
-
[(1, 0, -1, 1.
|
23092
|
-
(0, 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,
|
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.
|
23110
|
-
[(1, -0.
|
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
|
-
|
23147
|
-
|
23148
|
-
|
23149
|
-
|
23150
|
-
|
23151
|
-
|
23152
|
-
|
23153
|
-
|
23154
|
-
|
23155
|
-
|
23156
|
-
|
23157
|
-
|
23158
|
-
|
23159
|
-
|
23160
|
-
|
23161
|
-
|
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
|
-
|
23171
|
-
|
23172
|
-
|
23173
|
-
|
23174
|
-
|
23175
|
-
|
23176
|
-
|
23177
|
-
|
23178
|
-
|
23179
|
-
|
23180
|
-
|
23181
|
-
|
23182
|
-
|
23183
|
-
|
23184
|
-
|
23185
|
-
|
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
|
-
|
23196
|
-
|
23197
|
-
|
23198
|
-
|
23199
|
-
|
23200
|
-
|
23201
|
-
|
23202
|
-
|
23203
|
-
|
23204
|
-
|
23205
|
-
|
23206
|
-
|
23207
|
-
|
23208
|
-
|
23209
|
-
|
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
|
-
|
23219
|
-
|
23220
|
-
|
23221
|
-
|
23222
|
-
|
23223
|
-
|
23224
|
-
|
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))
|