passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.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.6.1rc1.dist-info/METADATA +292 -0
- passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
- passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
- passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
- passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
- passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
- passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
- sage/all__sagemath_graphs.py +39 -0
- sage/combinat/abstract_tree.py +2723 -0
- sage/combinat/all__sagemath_graphs.py +34 -0
- sage/combinat/binary_tree.py +5306 -0
- sage/combinat/cluster_algebra_quiver/all.py +22 -0
- sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
- sage/combinat/cluster_algebra_quiver/interact.py +124 -0
- sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
- sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
- sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
- sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
- sage/combinat/designs/MOLS_handbook_data.py +570 -0
- sage/combinat/designs/all.py +58 -0
- sage/combinat/designs/bibd.py +1655 -0
- sage/combinat/designs/block_design.py +1071 -0
- sage/combinat/designs/covering_array.py +269 -0
- sage/combinat/designs/covering_design.py +530 -0
- sage/combinat/designs/database.py +5615 -0
- sage/combinat/designs/design_catalog.py +122 -0
- sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/designs_pyx.pxd +21 -0
- sage/combinat/designs/designs_pyx.pyx +993 -0
- sage/combinat/designs/difference_family.py +3951 -0
- sage/combinat/designs/difference_matrices.py +279 -0
- sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
- sage/combinat/designs/ext_rep.py +1064 -0
- sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
- sage/combinat/designs/group_divisible_designs.py +361 -0
- sage/combinat/designs/incidence_structures.py +2357 -0
- sage/combinat/designs/latin_squares.py +581 -0
- sage/combinat/designs/orthogonal_arrays.py +2244 -0
- sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
- sage/combinat/designs/resolvable_bibd.py +815 -0
- sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
- sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/subhypergraph_search.pyx +530 -0
- sage/combinat/designs/twographs.py +306 -0
- sage/combinat/finite_state_machine.py +14874 -0
- sage/combinat/finite_state_machine_generators.py +2006 -0
- sage/combinat/graph_path.py +448 -0
- sage/combinat/interval_posets.py +3908 -0
- sage/combinat/nu_tamari_lattice.py +269 -0
- sage/combinat/ordered_tree.py +1446 -0
- sage/combinat/posets/all.py +46 -0
- sage/combinat/posets/bubble_shuffle.py +247 -0
- sage/combinat/posets/cartesian_product.py +493 -0
- sage/combinat/posets/d_complete.py +182 -0
- sage/combinat/posets/elements.py +273 -0
- sage/combinat/posets/forest.py +30 -0
- sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/hasse_cython.pyx +174 -0
- sage/combinat/posets/hasse_diagram.py +3672 -0
- sage/combinat/posets/hochschild_lattice.py +158 -0
- sage/combinat/posets/incidence_algebras.py +794 -0
- sage/combinat/posets/lattices.py +5117 -0
- sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/linear_extension_iterator.pyx +292 -0
- sage/combinat/posets/linear_extensions.py +1037 -0
- sage/combinat/posets/mobile.py +275 -0
- sage/combinat/posets/moebius_algebra.py +776 -0
- sage/combinat/posets/poset_examples.py +2178 -0
- sage/combinat/posets/posets.py +9360 -0
- sage/combinat/rooted_tree.py +1070 -0
- sage/combinat/shard_order.py +239 -0
- sage/combinat/tamari_lattices.py +384 -0
- sage/combinat/yang_baxter_graph.py +923 -0
- sage/databases/all__sagemath_graphs.py +1 -0
- sage/databases/knotinfo_db.py +1231 -0
- sage/ext_data/all__sagemath_graphs.py +1 -0
- sage/ext_data/graphs/graph_plot_js.html +330 -0
- 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/all.py +42 -0
- sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/asteroidal_triples.pyx +320 -0
- sage/graphs/base/all.py +1 -0
- sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/boost_graph.pxd +106 -0
- sage/graphs/base/boost_graph.pyx +3045 -0
- sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/c_graph.pxd +106 -0
- sage/graphs/base/c_graph.pyx +5096 -0
- sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/dense_graph.pxd +28 -0
- sage/graphs/base/dense_graph.pyx +801 -0
- sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/graph_backends.pxd +5 -0
- sage/graphs/base/graph_backends.pyx +797 -0
- sage/graphs/base/overview.py +85 -0
- sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/sparse_graph.pxd +90 -0
- sage/graphs/base/sparse_graph.pyx +1653 -0
- sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_dense_graph.pxd +5 -0
- sage/graphs/base/static_dense_graph.pyx +1032 -0
- sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_backend.pxd +27 -0
- sage/graphs/base/static_sparse_backend.pyx +1583 -0
- sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_graph.pxd +37 -0
- sage/graphs/base/static_sparse_graph.pyx +1375 -0
- sage/graphs/bipartite_graph.py +2732 -0
- sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/centrality.pyx +1038 -0
- sage/graphs/cographs.py +519 -0
- sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/comparability.pyx +851 -0
- sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/connectivity.pxd +157 -0
- sage/graphs/connectivity.pyx +4813 -0
- sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/convexity_properties.pxd +16 -0
- sage/graphs/convexity_properties.pyx +870 -0
- sage/graphs/digraph.py +4754 -0
- sage/graphs/digraph_generators.py +1993 -0
- sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/distances_all_pairs.pxd +12 -0
- sage/graphs/distances_all_pairs.pyx +2938 -0
- sage/graphs/domination.py +1363 -0
- sage/graphs/dot2tex_utils.py +100 -0
- sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/edge_connectivity.pyx +1215 -0
- sage/graphs/generators/all.py +1 -0
- sage/graphs/generators/basic.py +1769 -0
- sage/graphs/generators/chessboard.py +538 -0
- sage/graphs/generators/classical_geometries.py +1611 -0
- sage/graphs/generators/degree_sequence.py +235 -0
- sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generators/distance_regular.pyx +2846 -0
- sage/graphs/generators/families.py +4759 -0
- sage/graphs/generators/intersection.py +565 -0
- sage/graphs/generators/platonic_solids.py +262 -0
- sage/graphs/generators/random.py +2623 -0
- sage/graphs/generators/smallgraphs.py +5741 -0
- sage/graphs/generators/world_map.py +724 -0
- sage/graphs/generic_graph.py +26867 -0
- sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generic_graph_pyx.pxd +34 -0
- sage/graphs/generic_graph_pyx.pyx +1673 -0
- sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/genus.pyx +622 -0
- sage/graphs/graph.py +9645 -0
- sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_coloring.pyx +2284 -0
- sage/graphs/graph_database.py +1177 -0
- sage/graphs/graph_decompositions/all.py +1 -0
- sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
- sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
- sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
- sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
- sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
- sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/graph_products.pyx +508 -0
- sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
- sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
- sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
- sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
- sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
- sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
- sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
- sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
- sage/graphs/graph_editor.py +82 -0
- sage/graphs/graph_generators.py +3314 -0
- sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_generators_pyx.pyx +95 -0
- sage/graphs/graph_input.py +812 -0
- sage/graphs/graph_latex.py +2064 -0
- sage/graphs/graph_list.py +410 -0
- sage/graphs/graph_plot.py +1756 -0
- sage/graphs/graph_plot_js.py +338 -0
- sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/hyperbolicity.pyx +1704 -0
- sage/graphs/hypergraph_generators.py +364 -0
- sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/independent_sets.pxd +13 -0
- sage/graphs/independent_sets.pyx +402 -0
- sage/graphs/isgci.py +1033 -0
- sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/isoperimetric_inequalities.pyx +489 -0
- sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/line_graph.pyx +743 -0
- sage/graphs/lovasz_theta.py +77 -0
- sage/graphs/matching.py +1633 -0
- sage/graphs/matching_covered_graph.py +3590 -0
- sage/graphs/orientations.py +1489 -0
- sage/graphs/partial_cube.py +459 -0
- sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/path_enumeration.pyx +2040 -0
- sage/graphs/pq_trees.py +1129 -0
- sage/graphs/print_graphs.py +201 -0
- sage/graphs/schnyder.py +865 -0
- sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/spanning_tree.pyx +1457 -0
- sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/strongly_regular_db.pyx +3340 -0
- sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/traversals.pxd +9 -0
- sage/graphs/traversals.pyx +1872 -0
- sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/trees.pxd +15 -0
- sage/graphs/trees.pyx +310 -0
- sage/graphs/tutte_polynomial.py +713 -0
- sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/views.pyx +794 -0
- sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/weakly_chordal.pyx +604 -0
- sage/groups/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
- sage/knots/all.py +6 -0
- sage/knots/free_knotinfo_monoid.py +507 -0
- sage/knots/gauss_code.py +291 -0
- sage/knots/knot.py +682 -0
- sage/knots/knot_table.py +284 -0
- sage/knots/knotinfo.py +2900 -0
- sage/knots/link.py +4715 -0
- sage/sandpiles/all.py +13 -0
- sage/sandpiles/examples.py +225 -0
- sage/sandpiles/sandpile.py +6365 -0
- sage/topology/all.py +22 -0
- sage/topology/cell_complex.py +1214 -0
- sage/topology/cubical_complex.py +1976 -0
- sage/topology/delta_complex.py +1806 -0
- sage/topology/filtered_simplicial_complex.py +744 -0
- sage/topology/moment_angle_complex.py +823 -0
- sage/topology/simplicial_complex.py +5160 -0
- sage/topology/simplicial_complex_catalog.py +92 -0
- sage/topology/simplicial_complex_examples.py +1680 -0
- sage/topology/simplicial_complex_homset.py +205 -0
- sage/topology/simplicial_complex_morphism.py +836 -0
- sage/topology/simplicial_set.py +4102 -0
- sage/topology/simplicial_set_catalog.py +55 -0
- sage/topology/simplicial_set_constructions.py +2954 -0
- sage/topology/simplicial_set_examples.py +865 -0
- sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,625 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
# sage.doctest: needs sage.graphs
|
3
|
+
r"""
|
4
|
+
mutation_class
|
5
|
+
|
6
|
+
This file contains helper functions for compute the mutation class of a cluster algebra or quiver.
|
7
|
+
|
8
|
+
For the compendium on the cluster algebra and quiver package see [MS2011]_
|
9
|
+
|
10
|
+
AUTHORS:
|
11
|
+
|
12
|
+
- Gregg Musiker
|
13
|
+
- Christian Stump
|
14
|
+
"""
|
15
|
+
# ****************************************************************************
|
16
|
+
# Copyright (C) 2011 Gregg Musiker <musiker@math.mit.edu>
|
17
|
+
# Christian Stump <christian.stump@univie.ac.at>
|
18
|
+
#
|
19
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
20
|
+
# https://www.gnu.org/licenses/
|
21
|
+
# ****************************************************************************
|
22
|
+
|
23
|
+
import time
|
24
|
+
from sage.groups.perm_gps.partn_ref.refinement_graphs import search_tree, get_orbits
|
25
|
+
from sage.rings.integer_ring import ZZ
|
26
|
+
from sage.rings.infinity import infinity
|
27
|
+
from sage.graphs.digraph import DiGraph
|
28
|
+
from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import _edge_list_to_matrix
|
29
|
+
|
30
|
+
|
31
|
+
def _principal_part(mat):
|
32
|
+
"""
|
33
|
+
Return the principal part of a matrix.
|
34
|
+
|
35
|
+
INPUT:
|
36
|
+
|
37
|
+
- ``mat`` -- a matrix with at least as many rows as columns
|
38
|
+
|
39
|
+
OUTPUT: the top square part of the matrix ``mat``
|
40
|
+
|
41
|
+
EXAMPLES::
|
42
|
+
|
43
|
+
sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _principal_part
|
44
|
+
sage: M = Matrix([[1,2],[3,4],[5,6]]); M # needs sage.modules
|
45
|
+
[1 2]
|
46
|
+
[3 4]
|
47
|
+
[5 6]
|
48
|
+
sage: _principal_part(M) # needs sage.modules
|
49
|
+
[1 2]
|
50
|
+
[3 4]
|
51
|
+
"""
|
52
|
+
n, m = mat.ncols(), mat.nrows() - mat.ncols()
|
53
|
+
if m < 0:
|
54
|
+
raise ValueError('the input matrix has more columns than rows')
|
55
|
+
elif m == 0:
|
56
|
+
return mat
|
57
|
+
else:
|
58
|
+
return mat.submatrix(0, 0, n, n)
|
59
|
+
|
60
|
+
|
61
|
+
def _digraph_mutate(dg, k, frozen=None):
|
62
|
+
"""
|
63
|
+
Return a digraph obtained from ``dg`` by mutating at vertex ``k``.
|
64
|
+
|
65
|
+
Vertices can be labelled by anything, and frozen vertices must
|
66
|
+
be explicitly given.
|
67
|
+
|
68
|
+
INPUT:
|
69
|
+
|
70
|
+
- ``dg`` -- a digraph with integral edge labels with ``n+m`` vertices
|
71
|
+
- ``k`` -- the vertex at which ``dg`` is mutated
|
72
|
+
- ``frozen`` -- the list of frozen vertices (default: empty list)
|
73
|
+
|
74
|
+
EXAMPLES::
|
75
|
+
|
76
|
+
sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _digraph_mutate
|
77
|
+
sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
|
78
|
+
sage: dg = ClusterQuiver(['A',4]).digraph() # needs sage.modules
|
79
|
+
sage: dg.edges(sort=True) # needs sage.modules
|
80
|
+
[(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
|
81
|
+
sage: _digraph_mutate(dg,2).edges(sort=True) # needs sage.modules
|
82
|
+
[(0, 1, (1, -1)), (1, 2, (1, -1)), (3, 2, (1, -1))]
|
83
|
+
|
84
|
+
TESTS::
|
85
|
+
|
86
|
+
sage: dg = DiGraph([('a','b',(1,-1)),('c','a',(1,-1))])
|
87
|
+
sage: _digraph_mutate(dg,'a').edges(sort=True)
|
88
|
+
[('a', 'c', (1, -1)), ('b', 'a', (1, -1)), ('c', 'b', (1, -1))]
|
89
|
+
sage: _digraph_mutate(dg,'a',frozen=['b','c']).edges(sort=True)
|
90
|
+
[('a', 'c', (1, -1)), ('b', 'a', (1, -1))]
|
91
|
+
|
92
|
+
sage: dg = DiGraph([('a','b',(2,-2)),('c','a',(2,-2)),('b','c',(2,-2))])
|
93
|
+
sage: _digraph_mutate(dg,'a').edges(sort=True)
|
94
|
+
[('a', 'c', (2, -2)), ('b', 'a', (2, -2)), ('c', 'b', (2, -2))]
|
95
|
+
"""
|
96
|
+
# assert sorted(list(dg)) == list(range(n + m))
|
97
|
+
# this is not assumed anymore
|
98
|
+
if frozen is None:
|
99
|
+
frozen = []
|
100
|
+
|
101
|
+
edge_it = dg.incoming_edge_iterator(dg, True)
|
102
|
+
edges = {(v1, v2): label for v1, v2, label in edge_it}
|
103
|
+
edge_it = dg.incoming_edge_iterator([k], True)
|
104
|
+
in_edges = list(edge_it)
|
105
|
+
edge_it = dg.outgoing_edge_iterator([k], True)
|
106
|
+
out_edges = list(edge_it)
|
107
|
+
|
108
|
+
in_edges_new = [(v2, v1, (-label[1], -label[0]))
|
109
|
+
for (v1, v2, label) in in_edges]
|
110
|
+
out_edges_new = [(v2, v1, (-label[1], -label[0]))
|
111
|
+
for (v1, v2, label) in out_edges]
|
112
|
+
diag_edges_new = []
|
113
|
+
diag_edges_del = []
|
114
|
+
|
115
|
+
for (v1, v2, label1) in in_edges:
|
116
|
+
l11, l12 = label1
|
117
|
+
for (w1, w2, label2) in out_edges:
|
118
|
+
if v1 in frozen and w2 in frozen:
|
119
|
+
continue
|
120
|
+
l21, l22 = label2
|
121
|
+
if (v1, w2) in edges:
|
122
|
+
diag_edges_del.append((v1, w2))
|
123
|
+
a, b = edges[(v1, w2)]
|
124
|
+
a, b = a + l11 * l21, b - l12 * l22
|
125
|
+
diag_edges_new.append((v1, w2, (a, b)))
|
126
|
+
elif (w2, v1) in edges:
|
127
|
+
diag_edges_del.append((w2, v1))
|
128
|
+
a, b = edges[(w2, v1)]
|
129
|
+
a, b = b + l11 * l21, a - l12 * l22
|
130
|
+
if a < 0:
|
131
|
+
diag_edges_new.append((w2, v1, (b, a)))
|
132
|
+
elif a > 0:
|
133
|
+
diag_edges_new.append((v1, w2, (a, b)))
|
134
|
+
else:
|
135
|
+
a, b = l11 * l21, -l12 * l22
|
136
|
+
diag_edges_new.append((v1, w2, (a, b)))
|
137
|
+
|
138
|
+
del_edges = [tuple(ed[:2]) for ed in in_edges + out_edges]
|
139
|
+
del_edges += diag_edges_del
|
140
|
+
new_edges = in_edges_new + out_edges_new
|
141
|
+
new_edges += diag_edges_new
|
142
|
+
new_edges += [(v1, v2, edges[(v1, v2)]) for (v1, v2) in edges
|
143
|
+
if (v1, v2) not in del_edges]
|
144
|
+
|
145
|
+
dg_new = DiGraph()
|
146
|
+
dg_new.add_vertices(list(dg))
|
147
|
+
for v1, v2, label in new_edges:
|
148
|
+
dg_new.add_edge(v1, v2, label)
|
149
|
+
|
150
|
+
return dg_new
|
151
|
+
|
152
|
+
|
153
|
+
def _matrix_to_digraph(M):
|
154
|
+
"""
|
155
|
+
Return the digraph obtained from the matrix ``M``.
|
156
|
+
|
157
|
+
In order to generate a quiver, we assume that ``M`` is skew-symmetrizable.
|
158
|
+
|
159
|
+
EXAMPLES::
|
160
|
+
|
161
|
+
sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _matrix_to_digraph
|
162
|
+
sage: _matrix_to_digraph(matrix(3,[0,1,0,-1,0,-1,0,1,0])) # needs sage.modules
|
163
|
+
Digraph on 3 vertices
|
164
|
+
"""
|
165
|
+
n = M.ncols()
|
166
|
+
|
167
|
+
dg = DiGraph(sparse=True)
|
168
|
+
for i, j in M.nonzero_positions():
|
169
|
+
if i >= n:
|
170
|
+
a, b = M[i, j], -M[i, j]
|
171
|
+
else:
|
172
|
+
a, b = M[i, j], M[j, i]
|
173
|
+
if a > 0:
|
174
|
+
dg._backend.add_edge(i, j, (a, b), True)
|
175
|
+
elif i >= n:
|
176
|
+
dg._backend.add_edge(j, i, (-a, -b), True)
|
177
|
+
for i in range(M.nrows()):
|
178
|
+
if i not in dg:
|
179
|
+
dg.add_vertex(i)
|
180
|
+
return dg
|
181
|
+
|
182
|
+
|
183
|
+
def _dg_canonical_form(dg, frozen=None):
|
184
|
+
"""
|
185
|
+
Turn the digraph ``dg`` into its canonical form, and return the
|
186
|
+
corresponding isomorphism and the vertex orbits of the automorphism group.
|
187
|
+
|
188
|
+
The labels of ``dg`` are assumed to be integers between `0` and `n + m - 1`.
|
189
|
+
|
190
|
+
The canonical form has the following additional property: the frozen
|
191
|
+
vertices are the final labels.
|
192
|
+
|
193
|
+
.. WARNING:: The input ``dg`` is modified.
|
194
|
+
|
195
|
+
INPUT:
|
196
|
+
|
197
|
+
- ``dg`` -- a directed graph having edge labels (a, b) with a > 0
|
198
|
+
|
199
|
+
- ``frozen`` -- list (default: ``[]``) of frozen vertices
|
200
|
+
|
201
|
+
OUTPUT: dictionary {original label: canonical label}
|
202
|
+
|
203
|
+
- list of orbits of mutable vertices (using canonical labels)
|
204
|
+
|
205
|
+
EXAMPLES::
|
206
|
+
|
207
|
+
sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _dg_canonical_form
|
208
|
+
sage: dg = ClusterQuiver(['B',4]).digraph(); dg.edges(sort=True) # needs sage.modules
|
209
|
+
[(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -2))]
|
210
|
+
sage: _dg_canonical_form(dg); dg.edges(sort=True) # needs sage.modules
|
211
|
+
({0: 0, 1: 3, 2: 1, 3: 2}, [[0], [3], [1], [2]])
|
212
|
+
[(0, 3, (1, -1)), (1, 2, (1, -2)), (1, 3, (1, -1))]
|
213
|
+
|
214
|
+
TESTS::
|
215
|
+
|
216
|
+
sage: dg2 = ClusterQuiver(DiGraph({0:[1,2]})).digraph() # needs sage.modules
|
217
|
+
sage: _dg_canonical_form(dg2); dg2.edges(sort=True) # needs sage.modules
|
218
|
+
({0: 0, 1: 1, 2: 2}, [[0], [1, 2]])
|
219
|
+
[(0, 1, (1, -1)), (0, 2, (1, -1))]
|
220
|
+
|
221
|
+
sage: dg2 = ClusterQuiver(DiGraph({0:[1,2]})).digraph() # needs sage.modules
|
222
|
+
sage: _dg_canonical_form(dg2, frozen=[0]); dg2.edges(sort=True) # needs sage.modules
|
223
|
+
({0: 2, 1: 0, 2: 1}, [[2], [0, 1]])
|
224
|
+
[(2, 0, (1, -1)), (2, 1, (1, -1))]
|
225
|
+
|
226
|
+
sage: dg3 = ClusterQuiver(DiGraph({0:[1,2],1:[3]})).digraph() # needs sage.modules
|
227
|
+
sage: _dg_canonical_form(dg3, frozen=[0,3]); dg3.edges(sort=True) # needs sage.modules
|
228
|
+
({0: 2, 1: 1, 2: 0, 3: 3}, [[2], [1], [0], [3]])
|
229
|
+
[(1, 3, (1, -1)), (2, 0, (1, -1)), (2, 1, (1, -1))]
|
230
|
+
|
231
|
+
sage: dg3 = ClusterQuiver(DiGraph({2:[1,3],1:[0],3:[4]})).digraph() # needs sage.modules
|
232
|
+
sage: _dg_canonical_form(dg3, frozen=[4,0]); dg3.edges(sort=True) # needs sage.modules
|
233
|
+
({0: 4, 1: 1, 2: 0, 3: 2, 4: 3}, [[4, 3], [1, 2], [0]])
|
234
|
+
[(0, 1, (1, -1)), (0, 2, (1, -1)), (1, 4, (1, -1)), (2, 3, (1, -1))]
|
235
|
+
"""
|
236
|
+
vertices = list(dg)
|
237
|
+
n_plus_m = dg.order()
|
238
|
+
if frozen is not None:
|
239
|
+
partition = [[v for v in vertices if v not in frozen], frozen]
|
240
|
+
else:
|
241
|
+
frozen = []
|
242
|
+
partition = [ vertices ]
|
243
|
+
|
244
|
+
# here dg is modified by inserting new vertices at middle of edges
|
245
|
+
partition_add, edges = _graph_without_edge_labels(dg, vertices)
|
246
|
+
|
247
|
+
partition += partition_add
|
248
|
+
automorphism_group, _, iso = search_tree(dg, partition=partition, lab=True,
|
249
|
+
dig=True, certificate=True)
|
250
|
+
orbits = get_orbits(automorphism_group, n_plus_m)
|
251
|
+
orbits = [[iso[i] for i in orbit] for orbit in orbits]
|
252
|
+
|
253
|
+
removed = []
|
254
|
+
for v in iso:
|
255
|
+
if v not in vertices:
|
256
|
+
removed.append(v)
|
257
|
+
v1, _, _ = next(dg._backend.iterator_in_edges([v], True))
|
258
|
+
_, w2, _ = next(dg._backend.iterator_out_edges([v], True))
|
259
|
+
dg._backend.del_vertex(v)
|
260
|
+
add_index = True
|
261
|
+
index = 0
|
262
|
+
while add_index:
|
263
|
+
l = partition_add[index]
|
264
|
+
if v in l:
|
265
|
+
dg._backend.add_edge(v1, w2, edges[index], True)
|
266
|
+
add_index = False
|
267
|
+
index += 1
|
268
|
+
for v in removed:
|
269
|
+
del iso[v]
|
270
|
+
|
271
|
+
dg._backend.relabel(iso, True)
|
272
|
+
return iso, orbits
|
273
|
+
|
274
|
+
|
275
|
+
def _mutation_class_iter( dg, n, m, depth=infinity, return_dig6=False, show_depth=False, up_to_equivalence=True, sink_source=False ):
|
276
|
+
"""
|
277
|
+
Return an iterator for mutation class of dg with respect to several parameters.
|
278
|
+
|
279
|
+
.. NOTE:: assuming that the frozen vertices start at vertex n.
|
280
|
+
|
281
|
+
INPUT:
|
282
|
+
|
283
|
+
- ``dg`` -- a digraph with `n+m` vertices
|
284
|
+
- ``depth`` -- positive integer or infinity specifying (roughly) how many
|
285
|
+
steps away from the initial seed to mutate
|
286
|
+
- ``return_dig6`` -- indicates whether to convert digraph data to dig6
|
287
|
+
string data
|
288
|
+
- ``show_depth`` -- if ``True``, indicates that a running count of the
|
289
|
+
depth is to be displayed
|
290
|
+
- ``up_to_equivalence`` -- if ``True``, only one digraph for each
|
291
|
+
graph-isomorphism class is recorded
|
292
|
+
- ``sink_source`` -- if ``True``, only mutations at sinks or sources are
|
293
|
+
applied
|
294
|
+
|
295
|
+
EXAMPLES::
|
296
|
+
|
297
|
+
sage: # needs sage.modules
|
298
|
+
sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _mutation_class_iter
|
299
|
+
sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
|
300
|
+
sage: dg = ClusterQuiver(['A',[1,2],1]).digraph()
|
301
|
+
sage: itt = _mutation_class_iter(dg, 3,0)
|
302
|
+
sage: next(itt)[0].edges(sort=True)
|
303
|
+
[(0, 1, (1, -1)), (0, 2, (1, -1)), (1, 2, (1, -1))]
|
304
|
+
sage: next(itt)[0].edges(sort=True)
|
305
|
+
[(0, 2, (1, -1)), (1, 0, (2, -2)), (2, 1, (1, -1))]
|
306
|
+
"""
|
307
|
+
# assuming that the frozen vertices are at the end (from n to n + m - 1)
|
308
|
+
# assert sorted(dg) == list(range(n + m)) # commented out for speed
|
309
|
+
mlist = list(range(n, n + m))
|
310
|
+
|
311
|
+
timer = time.time()
|
312
|
+
depth_counter = 0
|
313
|
+
if up_to_equivalence:
|
314
|
+
iso, orbits = _dg_canonical_form( dg, mlist )
|
315
|
+
iso_inv = { iso[a]: a for a in iso }
|
316
|
+
|
317
|
+
dig6 = _digraph_to_dig6( dg, hashable=True )
|
318
|
+
dig6s = {}
|
319
|
+
if up_to_equivalence:
|
320
|
+
orbits = [ orbit[0] for orbit in orbits ]
|
321
|
+
dig6s[dig6] = [ orbits, [], iso_inv ]
|
322
|
+
else:
|
323
|
+
dig6s[dig6] = [list(range(n)), [] ]
|
324
|
+
if return_dig6:
|
325
|
+
yield (dig6, [])
|
326
|
+
else:
|
327
|
+
yield (dg, [])
|
328
|
+
|
329
|
+
gets_bigger = True
|
330
|
+
if show_depth:
|
331
|
+
timer2 = time.time()
|
332
|
+
dc = str(depth_counter)
|
333
|
+
dc += ' ' * (5 - len(dc))
|
334
|
+
nr = str(len(dig6s))
|
335
|
+
nr += ' ' * (10 - len(nr))
|
336
|
+
print(f"Depth: {dc} found: {nr} Time: {timer2 - timer:.2f} s")
|
337
|
+
|
338
|
+
while gets_bigger and depth_counter < depth:
|
339
|
+
gets_bigger = False
|
340
|
+
for key in list(dig6s):
|
341
|
+
mutation_indices = [ i for i in dig6s[key][0] if i < n ]
|
342
|
+
if mutation_indices:
|
343
|
+
dg = _dig6_to_digraph( key )
|
344
|
+
while mutation_indices:
|
345
|
+
i = mutation_indices.pop()
|
346
|
+
if not sink_source or _dg_is_sink_source( dg, i ):
|
347
|
+
dg_new = _digraph_mutate(dg, i, frozen=mlist)
|
348
|
+
if up_to_equivalence:
|
349
|
+
iso, orbits = _dg_canonical_form( dg_new, mlist )
|
350
|
+
i_new = iso[i]
|
351
|
+
iso_inv = { iso[a]: a for a in iso }
|
352
|
+
else:
|
353
|
+
i_new = i
|
354
|
+
dig6_new = _digraph_to_dig6( dg_new, hashable=True )
|
355
|
+
if dig6_new in dig6s:
|
356
|
+
if i_new in dig6s[dig6_new][0]:
|
357
|
+
dig6s[dig6_new][0].remove(i_new)
|
358
|
+
else:
|
359
|
+
gets_bigger = True
|
360
|
+
if up_to_equivalence:
|
361
|
+
orbits = [ orbit[0] for orbit in orbits if i_new not in orbit ]
|
362
|
+
iso_history = { a: dig6s[key][2][iso_inv[a]] for a in iso }
|
363
|
+
i_history = iso_history[i_new]
|
364
|
+
history = dig6s[key][1] + [i_history]
|
365
|
+
dig6s[dig6_new] = [orbits,history,iso_history]
|
366
|
+
else:
|
367
|
+
orbits = list(range(n))
|
368
|
+
del orbits[i_new]
|
369
|
+
history = dig6s[key][1] + [i_new]
|
370
|
+
dig6s[dig6_new] = [orbits,history]
|
371
|
+
if return_dig6:
|
372
|
+
yield (dig6_new,history)
|
373
|
+
else:
|
374
|
+
yield (dg_new,history)
|
375
|
+
depth_counter += 1
|
376
|
+
if show_depth and gets_bigger:
|
377
|
+
timer2 = time.time()
|
378
|
+
dc = str(depth_counter)
|
379
|
+
dc += ' ' * (5 - len(dc))
|
380
|
+
nr = str(len(dig6s))
|
381
|
+
nr += ' ' * (10 - len(nr))
|
382
|
+
print(f"Depth: {dc} found: {nr} Time: {timer2 - timer:.2f} s")
|
383
|
+
|
384
|
+
|
385
|
+
def _digraph_to_dig6(dg, hashable=False):
|
386
|
+
"""
|
387
|
+
Return the dig6 and edge data of the digraph dg.
|
388
|
+
|
389
|
+
INPUT:
|
390
|
+
|
391
|
+
- ``dg`` -- a digraph
|
392
|
+
- ``hashable`` -- boolean (default: ``False``); if ``True``, the edge
|
393
|
+
labels are turned into a dict
|
394
|
+
|
395
|
+
EXAMPLES::
|
396
|
+
|
397
|
+
sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _digraph_to_dig6
|
398
|
+
sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
|
399
|
+
sage: dg = ClusterQuiver(['A',4]).digraph() # needs sage.modules
|
400
|
+
sage: _digraph_to_dig6(dg) # needs sage.modules
|
401
|
+
('COD?', {})
|
402
|
+
"""
|
403
|
+
dig6 = dg.dig6_string()
|
404
|
+
D = {}
|
405
|
+
for E in dg._backend.iterator_in_edges(dg,True):
|
406
|
+
if E[2] != (1,-1):
|
407
|
+
D[ (E[0],E[1]) ] = E[2]
|
408
|
+
if hashable:
|
409
|
+
D = tuple( sorted( D.items() ) )
|
410
|
+
return (dig6,D)
|
411
|
+
|
412
|
+
|
413
|
+
def _dig6_to_digraph( dig6 ):
|
414
|
+
"""
|
415
|
+
Return the digraph obtained from the dig6 and edge data.
|
416
|
+
|
417
|
+
INPUT:
|
418
|
+
|
419
|
+
- ``dig6`` -- a pair ``(dig6, edges)`` where ``dig6`` is a string encoding a digraph and ``edges`` is a dict or tuple encoding edges
|
420
|
+
|
421
|
+
EXAMPLES::
|
422
|
+
|
423
|
+
sage: # needs sage.modules
|
424
|
+
sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _digraph_to_dig6
|
425
|
+
sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _dig6_to_digraph
|
426
|
+
sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
|
427
|
+
sage: dg = ClusterQuiver(['A',4]).digraph()
|
428
|
+
sage: data = _digraph_to_dig6(dg)
|
429
|
+
sage: _dig6_to_digraph(data)
|
430
|
+
Digraph on 4 vertices
|
431
|
+
sage: _dig6_to_digraph(data).edges(sort=True)
|
432
|
+
[(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
|
433
|
+
"""
|
434
|
+
dig6, edges = dig6
|
435
|
+
dg = DiGraph( dig6 )
|
436
|
+
if not isinstance(edges, dict):
|
437
|
+
edges = dict( edges )
|
438
|
+
for edge in dg._backend.iterator_in_edges(dg,False):
|
439
|
+
if edge in edges:
|
440
|
+
dg.set_edge_label( edge[0],edge[1],edges[edge] )
|
441
|
+
else:
|
442
|
+
dg.set_edge_label( edge[0],edge[1], (1,-1) )
|
443
|
+
return dg
|
444
|
+
|
445
|
+
|
446
|
+
def _dig6_to_matrix( dig6 ):
|
447
|
+
"""
|
448
|
+
Return the matrix obtained from the dig6 and edge data.
|
449
|
+
|
450
|
+
INPUT:
|
451
|
+
|
452
|
+
- ``dig6`` -- a pair ``(dig6, edges)`` where ``dig6`` is a string
|
453
|
+
encoding a digraph and ``edges`` is a dict or tuple encoding edges
|
454
|
+
|
455
|
+
EXAMPLES::
|
456
|
+
|
457
|
+
sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _digraph_to_dig6, _dig6_to_matrix
|
458
|
+
sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
|
459
|
+
sage: dg = ClusterQuiver(['A',4]).digraph() # needs sage.modules
|
460
|
+
sage: data = _digraph_to_dig6(dg) # needs sage.modules
|
461
|
+
sage: _dig6_to_matrix(data) # needs sage.modules
|
462
|
+
[ 0 1 0 0]
|
463
|
+
[-1 0 -1 0]
|
464
|
+
[ 0 1 0 1]
|
465
|
+
[ 0 0 -1 0]
|
466
|
+
"""
|
467
|
+
dg = _dig6_to_digraph(dig6)
|
468
|
+
return _edge_list_to_matrix(dg.edges(sort=True), list(range(dg.order())), [])
|
469
|
+
|
470
|
+
|
471
|
+
def _dg_is_sink_source( dg, v ):
|
472
|
+
"""
|
473
|
+
Return ``True`` iff the digraph dg has a sink or a source at vertex `v`.
|
474
|
+
|
475
|
+
INPUT:
|
476
|
+
|
477
|
+
- ``dg`` -- a digraph
|
478
|
+
- ``v`` -- a vertex of dg
|
479
|
+
|
480
|
+
EXAMPLES::
|
481
|
+
|
482
|
+
sage: # needs sage.modules
|
483
|
+
sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _dg_is_sink_source
|
484
|
+
sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
|
485
|
+
sage: dg = ClusterQuiver(['A',[1,2],1]).digraph()
|
486
|
+
sage: _dg_is_sink_source(dg, 0)
|
487
|
+
True
|
488
|
+
sage: _dg_is_sink_source(dg, 1)
|
489
|
+
True
|
490
|
+
sage: _dg_is_sink_source(dg, 2)
|
491
|
+
False
|
492
|
+
"""
|
493
|
+
in_edges = list(dg._backend.iterator_in_edges([v],True))
|
494
|
+
out_edges = list(dg._backend.iterator_out_edges([v],True))
|
495
|
+
return not ( in_edges and out_edges )
|
496
|
+
|
497
|
+
|
498
|
+
def _graph_without_edge_labels(dg, vertices):
|
499
|
+
"""
|
500
|
+
Expand the graph, by introducing new vertices in the middle of edges.
|
501
|
+
Return the corresponding partition of the new vertices.
|
502
|
+
|
503
|
+
There is one new vertex for each edge with label not equal to ``(1,-1)``.
|
504
|
+
These vertices are numbered starting from the smallest available integer.
|
505
|
+
|
506
|
+
Each edge having a non-trivial label is replaced by two consecutive edges,
|
507
|
+
passing through one new vertex.
|
508
|
+
|
509
|
+
EXAMPLES::
|
510
|
+
|
511
|
+
sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _graph_without_edge_labels
|
512
|
+
sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
|
513
|
+
sage: dg = ClusterQuiver(['B',4]).digraph(); dg.edges(sort=True) # needs sage.modules
|
514
|
+
[(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -2))]
|
515
|
+
sage: _graph_without_edge_labels(dg, range(4)); dg.edges(sort=True) # needs sage.modules
|
516
|
+
([[4]], ((1, -2),))
|
517
|
+
[(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 4, (1, -1)), (4, 3, (1, -1))]
|
518
|
+
"""
|
519
|
+
vertices = list(vertices)
|
520
|
+
edges = dg.edge_iterator(labels=True)
|
521
|
+
edge_labels = tuple(sorted({label for _, _, label in edges
|
522
|
+
if label != (1, -1)}))
|
523
|
+
edge_partition = [[] for _ in edge_labels]
|
524
|
+
i = 0
|
525
|
+
while i in vertices:
|
526
|
+
i += 1
|
527
|
+
|
528
|
+
# We have to pass the old vertices to the iterator.
|
529
|
+
# Otherwise the ``edge_iterator`` will use the ``vertices``
|
530
|
+
# of ``dg``, which are not well-defined, if we add vertices along the way.
|
531
|
+
for u, v, label in dg.edge_iterator(vertices=vertices, labels=True):
|
532
|
+
if label != (1, -1): # Ignore edges we just added.
|
533
|
+
index = edge_labels.index(label)
|
534
|
+
edge_partition[index].append(i)
|
535
|
+
dg._backend.add_edge(u, i, (1, -1), True)
|
536
|
+
dg._backend.add_edge(i, v, (1, -1), True)
|
537
|
+
dg._backend.del_edge(u, v, label, True)
|
538
|
+
i += 1
|
539
|
+
while i in vertices:
|
540
|
+
i += 1
|
541
|
+
return edge_partition, edge_labels
|
542
|
+
|
543
|
+
|
544
|
+
def _has_two_cycles( dg ):
|
545
|
+
"""
|
546
|
+
Return ``True`` if the input digraph has a 2-cycle and ``False`` otherwise.
|
547
|
+
|
548
|
+
EXAMPLES::
|
549
|
+
|
550
|
+
sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _has_two_cycles
|
551
|
+
sage: _has_two_cycles(DiGraph([[0,1],[1,0]]))
|
552
|
+
True
|
553
|
+
sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
|
554
|
+
sage: _has_two_cycles(ClusterQuiver(['A',3]).digraph()) # needs sage.modules
|
555
|
+
False
|
556
|
+
"""
|
557
|
+
edge_set = dg.edges(sort=True, labels=False)
|
558
|
+
for (v,w) in edge_set:
|
559
|
+
if (w,v) in edge_set:
|
560
|
+
return True
|
561
|
+
return False
|
562
|
+
|
563
|
+
|
564
|
+
def _is_valid_digraph_edge_set( edges, frozen=0 ):
|
565
|
+
"""
|
566
|
+
Return ``True`` if the input data is the edge set of a digraph for a quiver
|
567
|
+
(no loops, no 2-cycles, edge-labels of the specified format), and return
|
568
|
+
``False`` otherwise.
|
569
|
+
|
570
|
+
INPUT:
|
571
|
+
|
572
|
+
- ``frozen`` -- integer (default: 0); the number of frozen vertices
|
573
|
+
|
574
|
+
EXAMPLES::
|
575
|
+
|
576
|
+
sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _is_valid_digraph_edge_set
|
577
|
+
sage: _is_valid_digraph_edge_set( [[0,1,'a'],[2,3,(1,-1)]] )
|
578
|
+
The given digraph has edge labels which are not integral or integral 2-tuples.
|
579
|
+
False
|
580
|
+
sage: _is_valid_digraph_edge_set( [[0,1,None],[2,3,(1,-1)]] )
|
581
|
+
True
|
582
|
+
sage: _is_valid_digraph_edge_set( [[0,1,'a'],[2,3,(1,-1)],[3,2,(1,-1)]] )
|
583
|
+
The given digraph or edge list contains oriented 2-cycles.
|
584
|
+
False
|
585
|
+
"""
|
586
|
+
try:
|
587
|
+
dg = DiGraph()
|
588
|
+
dg.allow_multiple_edges(True)
|
589
|
+
dg.add_edges( edges )
|
590
|
+
|
591
|
+
# checks if the digraph contains loops
|
592
|
+
if dg.has_loops():
|
593
|
+
print("The given digraph or edge list contains loops")
|
594
|
+
return False
|
595
|
+
|
596
|
+
# checks if the digraph contains oriented 2-cycles
|
597
|
+
if _has_two_cycles( dg ):
|
598
|
+
print("The given digraph or edge list contains oriented 2-cycles.")
|
599
|
+
return False
|
600
|
+
|
601
|
+
# checks if all edge labels are 'None', positive integers or tuples of positive integers
|
602
|
+
if not all( i is None or ( i in ZZ and i > 0 ) or ( isinstance(i, tuple) and len(i) == 2 and i[0] in ZZ and i[1] in ZZ ) for i in dg.edge_labels() ):
|
603
|
+
print("The given digraph has edge labels which are not integral or integral 2-tuples.")
|
604
|
+
return False
|
605
|
+
|
606
|
+
# checks if all edge labels for multiple edges are 'None' or positive integers
|
607
|
+
if dg.has_multiple_edges():
|
608
|
+
for e in set( dg.multiple_edges(labels=False) ):
|
609
|
+
if not all( i is None or ( i in ZZ and i > 0 ) for i in dg.edge_label( e[0], e[1] ) ):
|
610
|
+
print("The given digraph or edge list contains multiple edges with non-integral labels.")
|
611
|
+
return False
|
612
|
+
|
613
|
+
n = dg.order() - frozen
|
614
|
+
if n < 0:
|
615
|
+
print("The number of frozen variables is larger than the number of vertices.")
|
616
|
+
return False
|
617
|
+
|
618
|
+
if any(e[0] >= n for e in dg.edges(sort=True, labels=False)):
|
619
|
+
print("The given digraph or edge list contains edges within the frozen vertices.")
|
620
|
+
return False
|
621
|
+
|
622
|
+
return True
|
623
|
+
except Exception:
|
624
|
+
print("Could not even build a digraph from the input data.")
|
625
|
+
return False
|