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.
Files changed (260) hide show
  1. passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
  2. passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
  3. passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
  4. passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
  5. passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
  6. passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
  7. passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  8. sage/all__sagemath_graphs.py +39 -0
  9. sage/combinat/abstract_tree.py +2723 -0
  10. sage/combinat/all__sagemath_graphs.py +34 -0
  11. sage/combinat/binary_tree.py +5306 -0
  12. sage/combinat/cluster_algebra_quiver/all.py +22 -0
  13. sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
  14. sage/combinat/cluster_algebra_quiver/interact.py +124 -0
  15. sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
  16. sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
  17. sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
  18. sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
  19. sage/combinat/designs/MOLS_handbook_data.py +570 -0
  20. sage/combinat/designs/all.py +58 -0
  21. sage/combinat/designs/bibd.py +1655 -0
  22. sage/combinat/designs/block_design.py +1071 -0
  23. sage/combinat/designs/covering_array.py +269 -0
  24. sage/combinat/designs/covering_design.py +530 -0
  25. sage/combinat/designs/database.py +5615 -0
  26. sage/combinat/designs/design_catalog.py +122 -0
  27. sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  28. sage/combinat/designs/designs_pyx.pxd +21 -0
  29. sage/combinat/designs/designs_pyx.pyx +993 -0
  30. sage/combinat/designs/difference_family.py +3951 -0
  31. sage/combinat/designs/difference_matrices.py +279 -0
  32. sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  33. sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
  34. sage/combinat/designs/ext_rep.py +1064 -0
  35. sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
  36. sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
  37. sage/combinat/designs/group_divisible_designs.py +361 -0
  38. sage/combinat/designs/incidence_structures.py +2357 -0
  39. sage/combinat/designs/latin_squares.py +581 -0
  40. sage/combinat/designs/orthogonal_arrays.py +2244 -0
  41. sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
  42. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
  43. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
  44. sage/combinat/designs/resolvable_bibd.py +815 -0
  45. sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
  46. sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
  47. sage/combinat/designs/subhypergraph_search.pyx +530 -0
  48. sage/combinat/designs/twographs.py +306 -0
  49. sage/combinat/finite_state_machine.py +14874 -0
  50. sage/combinat/finite_state_machine_generators.py +2006 -0
  51. sage/combinat/graph_path.py +448 -0
  52. sage/combinat/interval_posets.py +3908 -0
  53. sage/combinat/nu_tamari_lattice.py +269 -0
  54. sage/combinat/ordered_tree.py +1446 -0
  55. sage/combinat/posets/all.py +46 -0
  56. sage/combinat/posets/bubble_shuffle.py +247 -0
  57. sage/combinat/posets/cartesian_product.py +493 -0
  58. sage/combinat/posets/d_complete.py +182 -0
  59. sage/combinat/posets/elements.py +273 -0
  60. sage/combinat/posets/forest.py +30 -0
  61. sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
  62. sage/combinat/posets/hasse_cython.pyx +174 -0
  63. sage/combinat/posets/hasse_diagram.py +3672 -0
  64. sage/combinat/posets/hochschild_lattice.py +158 -0
  65. sage/combinat/posets/incidence_algebras.py +794 -0
  66. sage/combinat/posets/lattices.py +5117 -0
  67. sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
  68. sage/combinat/posets/linear_extension_iterator.pyx +292 -0
  69. sage/combinat/posets/linear_extensions.py +1037 -0
  70. sage/combinat/posets/mobile.py +275 -0
  71. sage/combinat/posets/moebius_algebra.py +776 -0
  72. sage/combinat/posets/poset_examples.py +2178 -0
  73. sage/combinat/posets/posets.py +9360 -0
  74. sage/combinat/rooted_tree.py +1070 -0
  75. sage/combinat/shard_order.py +239 -0
  76. sage/combinat/tamari_lattices.py +384 -0
  77. sage/combinat/yang_baxter_graph.py +923 -0
  78. sage/databases/all__sagemath_graphs.py +1 -0
  79. sage/databases/knotinfo_db.py +1231 -0
  80. sage/ext_data/all__sagemath_graphs.py +1 -0
  81. sage/ext_data/graphs/graph_plot_js.html +330 -0
  82. sage/ext_data/kenzo/CP2.txt +45 -0
  83. sage/ext_data/kenzo/CP3.txt +349 -0
  84. sage/ext_data/kenzo/CP4.txt +4774 -0
  85. sage/ext_data/kenzo/README.txt +49 -0
  86. sage/ext_data/kenzo/S4.txt +20 -0
  87. sage/graphs/all.py +42 -0
  88. sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
  89. sage/graphs/asteroidal_triples.pyx +320 -0
  90. sage/graphs/base/all.py +1 -0
  91. sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  92. sage/graphs/base/boost_graph.pxd +106 -0
  93. sage/graphs/base/boost_graph.pyx +3045 -0
  94. sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  95. sage/graphs/base/c_graph.pxd +106 -0
  96. sage/graphs/base/c_graph.pyx +5096 -0
  97. sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  98. sage/graphs/base/dense_graph.pxd +28 -0
  99. sage/graphs/base/dense_graph.pyx +801 -0
  100. sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
  101. sage/graphs/base/graph_backends.pxd +5 -0
  102. sage/graphs/base/graph_backends.pyx +797 -0
  103. sage/graphs/base/overview.py +85 -0
  104. sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  105. sage/graphs/base/sparse_graph.pxd +90 -0
  106. sage/graphs/base/sparse_graph.pyx +1653 -0
  107. sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  108. sage/graphs/base/static_dense_graph.pxd +5 -0
  109. sage/graphs/base/static_dense_graph.pyx +1032 -0
  110. sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
  111. sage/graphs/base/static_sparse_backend.pxd +27 -0
  112. sage/graphs/base/static_sparse_backend.pyx +1583 -0
  113. sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  114. sage/graphs/base/static_sparse_graph.pxd +37 -0
  115. sage/graphs/base/static_sparse_graph.pyx +1375 -0
  116. sage/graphs/bipartite_graph.py +2732 -0
  117. sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
  118. sage/graphs/centrality.pyx +1038 -0
  119. sage/graphs/cographs.py +519 -0
  120. sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
  121. sage/graphs/comparability.pyx +851 -0
  122. sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  123. sage/graphs/connectivity.pxd +157 -0
  124. sage/graphs/connectivity.pyx +4813 -0
  125. sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
  126. sage/graphs/convexity_properties.pxd +16 -0
  127. sage/graphs/convexity_properties.pyx +870 -0
  128. sage/graphs/digraph.py +4754 -0
  129. sage/graphs/digraph_generators.py +1993 -0
  130. sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
  131. sage/graphs/distances_all_pairs.pxd +12 -0
  132. sage/graphs/distances_all_pairs.pyx +2938 -0
  133. sage/graphs/domination.py +1363 -0
  134. sage/graphs/dot2tex_utils.py +100 -0
  135. sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  136. sage/graphs/edge_connectivity.pyx +1215 -0
  137. sage/graphs/generators/all.py +1 -0
  138. sage/graphs/generators/basic.py +1769 -0
  139. sage/graphs/generators/chessboard.py +538 -0
  140. sage/graphs/generators/classical_geometries.py +1611 -0
  141. sage/graphs/generators/degree_sequence.py +235 -0
  142. sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
  143. sage/graphs/generators/distance_regular.pyx +2846 -0
  144. sage/graphs/generators/families.py +4759 -0
  145. sage/graphs/generators/intersection.py +565 -0
  146. sage/graphs/generators/platonic_solids.py +262 -0
  147. sage/graphs/generators/random.py +2623 -0
  148. sage/graphs/generators/smallgraphs.py +5741 -0
  149. sage/graphs/generators/world_map.py +724 -0
  150. sage/graphs/generic_graph.py +26867 -0
  151. sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  152. sage/graphs/generic_graph_pyx.pxd +34 -0
  153. sage/graphs/generic_graph_pyx.pyx +1673 -0
  154. sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
  155. sage/graphs/genus.pyx +622 -0
  156. sage/graphs/graph.py +9645 -0
  157. sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
  158. sage/graphs/graph_coloring.pyx +2284 -0
  159. sage/graphs/graph_database.py +1177 -0
  160. sage/graphs/graph_decompositions/all.py +1 -0
  161. sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  162. sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
  163. sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
  164. sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
  165. sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  166. sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
  167. sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
  168. sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
  169. sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
  170. sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
  171. sage/graphs/graph_decompositions/graph_products.pyx +508 -0
  172. sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  173. sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
  174. sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
  175. sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  176. sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
  177. sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
  178. sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  179. sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
  180. sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
  181. sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
  182. sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
  183. sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
  184. sage/graphs/graph_editor.py +82 -0
  185. sage/graphs/graph_generators.py +3314 -0
  186. sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  187. sage/graphs/graph_generators_pyx.pyx +95 -0
  188. sage/graphs/graph_input.py +812 -0
  189. sage/graphs/graph_latex.py +2064 -0
  190. sage/graphs/graph_list.py +410 -0
  191. sage/graphs/graph_plot.py +1756 -0
  192. sage/graphs/graph_plot_js.py +338 -0
  193. sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
  194. sage/graphs/hyperbolicity.pyx +1704 -0
  195. sage/graphs/hypergraph_generators.py +364 -0
  196. sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  197. sage/graphs/independent_sets.pxd +13 -0
  198. sage/graphs/independent_sets.pyx +402 -0
  199. sage/graphs/isgci.py +1033 -0
  200. sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
  201. sage/graphs/isoperimetric_inequalities.pyx +489 -0
  202. sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  203. sage/graphs/line_graph.pyx +743 -0
  204. sage/graphs/lovasz_theta.py +77 -0
  205. sage/graphs/matching.py +1633 -0
  206. sage/graphs/matching_covered_graph.py +3590 -0
  207. sage/graphs/orientations.py +1489 -0
  208. sage/graphs/partial_cube.py +459 -0
  209. sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
  210. sage/graphs/path_enumeration.pyx +2040 -0
  211. sage/graphs/pq_trees.py +1129 -0
  212. sage/graphs/print_graphs.py +201 -0
  213. sage/graphs/schnyder.py +865 -0
  214. sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
  215. sage/graphs/spanning_tree.pyx +1457 -0
  216. sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
  217. sage/graphs/strongly_regular_db.pyx +3340 -0
  218. sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
  219. sage/graphs/traversals.pxd +9 -0
  220. sage/graphs/traversals.pyx +1872 -0
  221. sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
  222. sage/graphs/trees.pxd +15 -0
  223. sage/graphs/trees.pyx +310 -0
  224. sage/graphs/tutte_polynomial.py +713 -0
  225. sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
  226. sage/graphs/views.pyx +794 -0
  227. sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
  228. sage/graphs/weakly_chordal.pyx +604 -0
  229. sage/groups/all__sagemath_graphs.py +1 -0
  230. sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
  231. sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
  232. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
  233. sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
  234. sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
  235. sage/knots/all.py +6 -0
  236. sage/knots/free_knotinfo_monoid.py +507 -0
  237. sage/knots/gauss_code.py +291 -0
  238. sage/knots/knot.py +682 -0
  239. sage/knots/knot_table.py +284 -0
  240. sage/knots/knotinfo.py +2900 -0
  241. sage/knots/link.py +4715 -0
  242. sage/sandpiles/all.py +13 -0
  243. sage/sandpiles/examples.py +225 -0
  244. sage/sandpiles/sandpile.py +6365 -0
  245. sage/topology/all.py +22 -0
  246. sage/topology/cell_complex.py +1214 -0
  247. sage/topology/cubical_complex.py +1976 -0
  248. sage/topology/delta_complex.py +1806 -0
  249. sage/topology/filtered_simplicial_complex.py +744 -0
  250. sage/topology/moment_angle_complex.py +823 -0
  251. sage/topology/simplicial_complex.py +5160 -0
  252. sage/topology/simplicial_complex_catalog.py +92 -0
  253. sage/topology/simplicial_complex_examples.py +1680 -0
  254. sage/topology/simplicial_complex_homset.py +205 -0
  255. sage/topology/simplicial_complex_morphism.py +836 -0
  256. sage/topology/simplicial_set.py +4102 -0
  257. sage/topology/simplicial_set_catalog.py +55 -0
  258. sage/topology/simplicial_set_constructions.py +2954 -0
  259. sage/topology/simplicial_set_examples.py +865 -0
  260. sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,2290 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ # sage.doctest: needs sage.graphs sage.modules
3
+ r"""
4
+ Quiver
5
+
6
+ A *quiver* is an oriented graph without loops, two-cycles, or multiple
7
+ edges. The edges are labelled by pairs `(i,-j)` (with `i` and `j` being
8
+ positive integers) such that the matrix `M = (m_{ab})` with
9
+ `m_{ab} = i, m_{ba} = -j` for an edge `(i,-j)` between vertices
10
+ `a` and `b` is skew-symmetrizable.
11
+
12
+ .. WARNING::
13
+
14
+ This is not the standard definition of a quiver. Normally, in
15
+ cluster algebra theory, a quiver is defined as an oriented graph
16
+ without loops and two-cycles but with multiple edges allowed; the
17
+ edges are unlabelled. This notion of quivers, however, can be seen
18
+ as a particular case of our notion of quivers. Namely, if we have
19
+ a quiver (in the regular sense of this word) with (precisely)
20
+ `i` edges from `a` to `b`, then we represent it by a quiver
21
+ (in our sense of this word) with an edge from `a` to `b` labelled
22
+ by the pair `(i,-i)`.
23
+
24
+ For the compendium on the cluster algebra and quiver package see [MS2011]_
25
+
26
+ AUTHORS:
27
+
28
+ - Gregg Musiker
29
+ - Christian Stump
30
+
31
+ .. SEEALSO::
32
+
33
+ For mutation types of combinatorial quivers, see :meth:`~sage.combinat.cluster_algebra_quiver.quiver_mutation_type.QuiverMutationType`.
34
+ Cluster seeds are closely related to :meth:`~sage.combinat.cluster_algebra_quiver.cluster_seed.ClusterSeed`.
35
+ """
36
+ # ****************************************************************************
37
+ # Copyright (C) 2011 Gregg Musiker <musiker@math.mit.edu>
38
+ # Christian Stump <christian.stump@univie.ac.at>
39
+ #
40
+ # Distributed under the terms of the GNU General Public License (GPL)
41
+ # https://www.gnu.org/licenses/
42
+ # ****************************************************************************
43
+ from copy import copy
44
+ from itertools import product
45
+
46
+ from sage.arith.misc import gcd
47
+ from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import QuiverMutationType, QuiverMutationType_Irreducible, QuiverMutationType_Reducible, _edge_list_to_matrix
48
+ from sage.combinat.cluster_algebra_quiver.mutation_class import _principal_part, _digraph_mutate, _matrix_to_digraph, _dg_canonical_form, _mutation_class_iter, _digraph_to_dig6, _dig6_to_matrix
49
+ from sage.combinat.cluster_algebra_quiver.mutation_type import _connected_mutation_type, _mutation_type_from_data, is_mutation_finite
50
+ from sage.combinat.cluster_algebra_quiver.interact import cluster_interact
51
+ from sage.graphs.digraph import DiGraph
52
+ from sage.graphs.graph import Graph
53
+ from sage.graphs.views import EdgesView
54
+ from sage.misc.lazy_import import lazy_import
55
+ from sage.misc.misc_c import prod
56
+ from sage.rings.infinity import infinity
57
+ from sage.rings.integer_ring import ZZ
58
+ from sage.rings.polynomial.polynomial_ring import polygen
59
+ from sage.rings.rational_field import QQ
60
+ from sage.structure.sage_object import SageObject
61
+
62
+ lazy_import('sage.modules.free_module_element', 'vector')
63
+ lazy_import('sage.matrix.constructor', 'matrix')
64
+
65
+
66
+ class ClusterQuiver(SageObject):
67
+ """
68
+ The *quiver* associated to an *exchange matrix*.
69
+
70
+ INPUT:
71
+
72
+ - ``data`` -- can be any of the following::
73
+
74
+ - :class:`QuiverMutationType`
75
+
76
+ - :class:`str` -- string representing a :class:`QuiverMutationType`
77
+ or a common quiver type (see Examples)
78
+
79
+ - :class:`ClusterQuiver`
80
+
81
+ - :class:`Matrix` -- a skew-symmetrizable matrix
82
+
83
+ - :class:`DiGraph` -- must be the input data for a quiver
84
+
85
+ - List of edges -- must be the edge list of a digraph for a quiver
86
+
87
+ - ``frozen`` -- (default: ``None``) sets the list of frozen variables
88
+ if the input type is a :class:`DiGraph`, it is ignored otherwise
89
+
90
+ - ``user_labels`` -- (default: ``None``) sets the names of the labels for
91
+ the vertices of the quiver if the input type is not a :class:`DiGraph`;
92
+ otherwise it is ignored
93
+
94
+ EXAMPLES:
95
+
96
+ From a :class:`QuiverMutationType`::
97
+
98
+ sage: Q = ClusterQuiver(['A',5]); Q
99
+ Quiver on 5 vertices of type ['A', 5]
100
+
101
+ sage: Q = ClusterQuiver(['B',2]); Q
102
+ Quiver on 2 vertices of type ['B', 2]
103
+ sage: Q2 = ClusterQuiver(['C',2]); Q2
104
+ Quiver on 2 vertices of type ['B', 2]
105
+ sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
106
+ True
107
+ sage: MT = Q2.mutation_type(); MT.standard_quiver() == Q2
108
+ False
109
+
110
+ sage: Q = ClusterQuiver(['A',[2,5],1]); Q
111
+ Quiver on 7 vertices of type ['A', [2, 5], 1]
112
+
113
+ sage: Q = ClusterQuiver(['A', [5,0],1]); Q
114
+ Quiver on 5 vertices of type ['D', 5]
115
+ sage: Q.is_finite()
116
+ True
117
+ sage: Q.is_acyclic()
118
+ False
119
+
120
+ sage: Q = ClusterQuiver(['F', 4, [2,1]]); Q
121
+ Quiver on 6 vertices of type ['F', 4, [1, 2]]
122
+ sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
123
+ False
124
+ sage: dg = Q.digraph(); Q.mutate([2,1,4,0,5,3])
125
+ sage: dg2 = Q.digraph(); dg2.is_isomorphic(dg,edge_labels=True)
126
+ False
127
+ sage: dg2.is_isomorphic(MT.standard_quiver().digraph(),edge_labels=True)
128
+ True
129
+
130
+ sage: Q = ClusterQuiver(['G',2, (3,1)]); Q
131
+ Quiver on 4 vertices of type ['G', 2, [1, 3]]
132
+ sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
133
+ False
134
+
135
+ sage: Q = ClusterQuiver(['GR',[3,6]]); Q
136
+ Quiver on 4 vertices of type ['D', 4]
137
+ sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
138
+ False
139
+
140
+ sage: Q = ClusterQuiver(['GR',[3,7]]); Q
141
+ Quiver on 6 vertices of type ['E', 6]
142
+
143
+ sage: Q = ClusterQuiver(['TR',2]); Q
144
+ Quiver on 3 vertices of type ['A', 3]
145
+ sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
146
+ False
147
+ sage: Q.mutate([1,0]); MT.standard_quiver() == Q
148
+ True
149
+
150
+ sage: Q = ClusterQuiver(['TR',3]); Q
151
+ Quiver on 6 vertices of type ['D', 6]
152
+ sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
153
+ False
154
+
155
+ From a :class:`ClusterQuiver`::
156
+
157
+ sage: Q = ClusterQuiver(['A',[2,5],1]); Q
158
+ Quiver on 7 vertices of type ['A', [2, 5], 1]
159
+ sage: T = ClusterQuiver(Q); T
160
+ Quiver on 7 vertices of type ['A', [2, 5], 1]
161
+
162
+ From a Matrix::
163
+
164
+ sage: Q = ClusterQuiver(['A',[2,5],1]); Q
165
+ Quiver on 7 vertices of type ['A', [2, 5], 1]
166
+ sage: T = ClusterQuiver(Q._M); T
167
+ Quiver on 7 vertices
168
+
169
+ sage: Q = ClusterQuiver(matrix([[0,1,-1],[-1,0,1],[1,-1,0],[1,2,3]])); Q
170
+ Quiver on 4 vertices with 1 frozen vertex
171
+
172
+ sage: Q = ClusterQuiver(matrix([])); Q
173
+ Quiver without vertices
174
+
175
+ From a DiGraph::
176
+
177
+ sage: Q = ClusterQuiver(['A',[2,5],1]); Q
178
+ Quiver on 7 vertices of type ['A', [2, 5], 1]
179
+ sage: T = ClusterQuiver(Q._digraph); T
180
+ Quiver on 7 vertices
181
+
182
+ sage: Q = ClusterQuiver(DiGraph([[1,2],[2,3],[3,4],[4,1]])); Q
183
+ Quiver on 4 vertices
184
+
185
+ sage: Q = ClusterQuiver(DiGraph([['a', 'b'], ['b', 'c'], ['c', 'd'], ['d', 'e']]),
186
+ ....: frozen=['c']); Q
187
+ Quiver on 5 vertices with 1 frozen vertex
188
+ sage: Q.mutation_type()
189
+ [ ['A', 2], ['A', 2] ]
190
+ sage: Q
191
+ Quiver on 5 vertices of type [ ['A', 2], ['A', 2] ]
192
+ with 1 frozen vertex
193
+
194
+ From a List of edges::
195
+
196
+ sage: Q = ClusterQuiver(['A',[2,5],1]); Q
197
+ Quiver on 7 vertices of type ['A', [2, 5], 1]
198
+ sage: T = ClusterQuiver(Q._digraph.edges(sort=True)); T
199
+ Quiver on 7 vertices
200
+
201
+ sage: Q = ClusterQuiver([[1, 2], [2, 3], [3, 4], [4, 1]]); Q
202
+ Quiver on 4 vertices
203
+
204
+ TESTS::
205
+
206
+ sage: Q = ClusterQuiver(DiGraph([[1,1]]))
207
+ Traceback (most recent call last):
208
+ ...
209
+ ValueError: cannot add edge from 1 to 1 in graph without loops
210
+
211
+ sage: Q = ClusterQuiver([[1,1]])
212
+ Traceback (most recent call last):
213
+ ...
214
+ ValueError: cannot add edge from 1 to 1 in graph without loops
215
+
216
+ sage: Q = ClusterQuiver(DiGraph([[1, 0],[0,1]]))
217
+ Traceback (most recent call last):
218
+ ...
219
+ ValueError: the input DiGraph contains two-cycles
220
+
221
+ sage: Q = ClusterQuiver('whatever')
222
+ Traceback (most recent call last):
223
+ ...
224
+ ValueError: the input data was not recognized
225
+ """
226
+
227
+ def __init__(self, data, frozen=None, user_labels=None) -> None:
228
+ """
229
+ TESTS::
230
+
231
+ sage: Q = ClusterQuiver(['A',4])
232
+ sage: TestSuite(Q).run()
233
+ """
234
+ from sage.combinat.cluster_algebra_quiver.cluster_seed import ClusterSeed
235
+ from sage.structure.element import Matrix
236
+
237
+ if isinstance(user_labels, list):
238
+ user_labels = [tuple(x) if isinstance(x, list) else x
239
+ for x in user_labels]
240
+ elif isinstance(user_labels, dict):
241
+ user_labels = {x: tuple(label) if isinstance(label, list)
242
+ else label
243
+ for x, label in user_labels.items()}
244
+
245
+ # constructs a quiver from a mutation type
246
+ if isinstance(data, (QuiverMutationType_Irreducible,
247
+ QuiverMutationType_Reducible)):
248
+ if frozen is not None:
249
+ print('The input specifies a mutation type, so the'
250
+ ' additional parameter frozen is ignored.'
251
+ ' Use set_frozen to freeze vertices.')
252
+
253
+ mutation_type = data
254
+ self.__init__(mutation_type.standard_quiver())
255
+ if user_labels:
256
+ self.relabel(user_labels)
257
+ self._nlist = list(user_labels)
258
+
259
+ # constructs a quiver from string representing a mutation type
260
+ # or a common quiver type (see Examples)
261
+ # NOTE: for now, any string representing a *reducible type* is
262
+ # coerced into the standard quiver, but there is now more flexibility
263
+ # in how to input a connected (irreducible) quiver.
264
+ elif (isinstance(data, (list, tuple))
265
+ and (isinstance(data[0], str) or
266
+ all(isinstance(comp, (list, tuple))
267
+ and isinstance(comp[0], str) for comp in data))):
268
+ if frozen is not None:
269
+ print('The input specifies a mutation type, so the additional'
270
+ ' parameter frozen is ignored. Use set_frozen to freeze vertices.')
271
+ mutation_type = QuiverMutationType(data)
272
+
273
+ # The command QuiverMutationType_Irreducible (which is not imported
274
+ # globally) already creates the desired digraph as long as we
275
+ # bypass the mutation type checking of QuiverMutationType and
276
+ # format the input appropriately. Thus we handle several
277
+ # special cases this way.
278
+ if len(data) == 2 and isinstance(data[0], str):
279
+ d0, d1 = data
280
+ if d0 == 'TR' or d0 == 'GR' or (d0 == 'C' and d1 == 2):
281
+ if d1 in ZZ:
282
+ quiv = ClusterQuiver(QuiverMutationType_Irreducible(d0, d1)._digraph)
283
+ quiv._mutation_type = mutation_type
284
+ self.__init__(quiv)
285
+ elif isinstance(d1, list):
286
+ quiv = ClusterQuiver(QuiverMutationType_Irreducible(d0, tuple(d1))._digraph)
287
+ quiv._mutation_type = mutation_type
288
+ self.__init__(quiv)
289
+ else:
290
+ self.__init__(mutation_type.standard_quiver())
291
+ elif len(data) == 3 and isinstance(data[0], str):
292
+ d0, d1, d2 = data
293
+ if ((d0 == 'F' and d1 == 4 and d2 == [2, 1]) or
294
+ (d0 == 'G' and d1 == 2 and d2 == [3, 1])):
295
+ quiv = ClusterQuiver(QuiverMutationType_Irreducible(d0, d1, tuple(d2))._digraph)
296
+ quiv._mutation_type = mutation_type
297
+ self.__init__(quiv)
298
+ elif ((d0 == 'F' and d1 == 4 and d2 == (2, 1)) or
299
+ (d0 == 'G' and d1 == 2 and d2 == (3, 1))):
300
+ quiv = ClusterQuiver(QuiverMutationType_Irreducible(d0, d1, d2)._digraph)
301
+ quiv._mutation_type = mutation_type
302
+ self.__init__(quiv)
303
+ elif d0 == 'A' and isinstance(d1, list) and d2 == 1:
304
+ if len(d1) == 2 and min(d1) == 0:
305
+ quiv = ClusterQuiver(QuiverMutationType_Irreducible(d0, tuple(d1), d2)._digraph)
306
+ quiv._mutation_type = mutation_type
307
+ self.__init__(quiv)
308
+ else:
309
+ self.__init__(mutation_type.standard_quiver())
310
+
311
+ elif d0 == 'A' and isinstance(d1, tuple) and d2 == 1:
312
+ if len(d1) == 2 and min(d1) == 0:
313
+ quiv = ClusterQuiver(QuiverMutationType_Irreducible(d0, d1, d2)._digraph)
314
+ quiv._mutation_type = mutation_type
315
+ self.__init__(quiv)
316
+ else:
317
+ self.__init__(mutation_type.standard_quiver())
318
+
319
+ else:
320
+ self.__init__(mutation_type.standard_quiver())
321
+ else:
322
+ self.__init__(mutation_type.standard_quiver())
323
+
324
+ if user_labels:
325
+ if isinstance(user_labels, dict):
326
+ self._nlist = list(user_labels)
327
+ else:
328
+ self._nlist = user_labels
329
+
330
+ self.relabel(self._nlist)
331
+
332
+ # constructs a quiver from a cluster seed
333
+ elif isinstance(data, ClusterSeed):
334
+ self.__init__(data.quiver())
335
+
336
+ # constructs a quiver from a quiver
337
+ elif isinstance(data, ClusterQuiver):
338
+ if frozen is not None:
339
+ print('The input data is a quiver, therefore the additional'
340
+ ' parameter frozen is ignored. Use set_frozen to freeze vertices.')
341
+
342
+ self._M = copy(data._M)
343
+ self._M.set_immutable()
344
+ self._n = data._n
345
+ self._m = data._m
346
+ self._mlist = list(data._mlist)
347
+ self._nlist = list(data._nlist)
348
+ self._digraph = copy(data._digraph)
349
+ self._vertex_dictionary = data._vertex_dictionary
350
+ self._mutation_type = data._mutation_type
351
+ self._description = data._description
352
+
353
+ # constructs a quiver from a matrix
354
+ elif isinstance(data, Matrix):
355
+ if not _principal_part(data).is_skew_symmetrizable(positive=True):
356
+ raise ValueError('The principal part of the matrix data must be skew-symmetrizable.')
357
+
358
+ if frozen is not None:
359
+ print('The input data is a matrix, therefore the additional parameter frozen is ignored. Frozen vertices read off accordingly if the matrix is not square.')
360
+
361
+ self._M = copy(data).sparse_matrix()
362
+ self._M.set_immutable()
363
+ self._n = n = self._M.ncols()
364
+ self._m = m = self._M.nrows() - self._n
365
+ self._digraph = _matrix_to_digraph(self._M)
366
+ self._vertex_dictionary = {}
367
+ self._mutation_type = None
368
+
369
+ if user_labels:
370
+ if isinstance(user_labels, dict):
371
+ self._nlist = list(user_labels)[0:n]
372
+ self._mlist = list(user_labels)[n:n+m]
373
+ elif isinstance(user_labels, list):
374
+ self._nlist = user_labels[0:n]
375
+ self._mlist = user_labels[n:n+m]
376
+ self._digraph.relabel(self._nlist + self._mlist)
377
+ else:
378
+ self._mlist = list(range(n, n+m))
379
+ self._nlist = list(range(n))
380
+ if n+m == 0:
381
+ self._description = 'Quiver without vertices'
382
+ elif n+m == 1:
383
+ self._description = 'Quiver on 1 vertex'
384
+ else:
385
+ self._description = 'Quiver on %d vertices' % (n+m)
386
+
387
+ # constructs a quiver from a digraph
388
+ elif isinstance(data, DiGraph):
389
+ if frozen is None:
390
+ frozen = []
391
+ if not isinstance(frozen, (list, tuple)):
392
+ raise ValueError("'frozen' must be a list of vertices")
393
+ frozen = set(frozen)
394
+ if not frozen.issubset(data.vertex_iterator()):
395
+ raise ValueError("frozen elements must be vertices")
396
+
397
+ mlist = self._mlist = list(frozen)
398
+ m = self._m = len(mlist)
399
+
400
+ try:
401
+ nlist = sorted(x for x in data if x not in frozen)
402
+ except TypeError:
403
+ nlist = sorted([x for x in data if x not in frozen], key=str)
404
+ self._nlist = nlist
405
+ n = self._n = len(nlist)
406
+ labelDict = {x: i for i, x in enumerate(nlist + mlist)}
407
+
408
+ dg = copy(data)
409
+ if data.has_loops():
410
+ raise ValueError("the input DiGraph contains a loop")
411
+
412
+ edges = set(data.edge_iterator(labels=False))
413
+ if any((b, a) in edges for (a, b) in edges):
414
+ raise ValueError("the input DiGraph contains two-cycles")
415
+
416
+ dg_labelling = False
417
+ if not set(dg.vertex_iterator()) == set(range(n + m)):
418
+ # relabelling to integers
419
+ # frozen vertices must be preserved
420
+ dg_labelling = nlist + mlist
421
+ dg.relabel(labelDict)
422
+
423
+ multiple_edges = dg.multiple_edges()
424
+ if multiple_edges:
425
+ multi_edges = {}
426
+ for v1, v2, label in multiple_edges:
427
+ if label not in ZZ:
428
+ raise ValueError("the input DiGraph contains multiple"
429
+ " edges labeled by non-integers")
430
+ elif (v1, v2) in multi_edges:
431
+ multi_edges[(v1, v2)] += label
432
+ else:
433
+ multi_edges[(v1, v2)] = label
434
+ dg.delete_edge(v1, v2)
435
+ dg.add_edges([(v1, v2, multi_edges[(v1, v2)])
436
+ for v1, v2 in multi_edges])
437
+
438
+ for e0, e1, lab in dg.edge_iterator():
439
+ if e0 >= n and e1 >= n:
440
+ raise ValueError("the input digraph contains edges"
441
+ " within the frozen vertices")
442
+ if lab is None:
443
+ lab = (1, -1)
444
+ dg.set_edge_label(e0, e1, lab)
445
+ elif lab in ZZ:
446
+ lab = (lab, -lab)
447
+ dg.set_edge_label(e0, e1, lab)
448
+ elif isinstance(lab, list) and len(lab) != 2:
449
+ raise ValueError("the input digraph contains an edge with"
450
+ " the wrong type of list as a label")
451
+ elif isinstance(lab, list) and len(lab) == 2:
452
+ lab = tuple(lab)
453
+ dg.set_edge_label(e0, e1, lab)
454
+ elif (e0 >= n or e1 >= n) and not lab[0] == -lab[1]:
455
+ raise ValueError("the input digraph contains an edge to or"
456
+ " from a frozen vertex which is not skew-symmetric")
457
+ if lab[0] < 0:
458
+ raise ValueError("the input digraph contains an edge of "
459
+ "the form (a,-b) with negative a")
460
+
461
+ M = _edge_list_to_matrix(dg.edge_iterator(), list(range(n)),
462
+ list(range(n, n + m)))
463
+ if not _principal_part(M).is_skew_symmetrizable(positive=True):
464
+ raise ValueError("the input digraph must be skew-symmetrizable")
465
+
466
+ self._digraph = dg
467
+ self._vertex_dictionary = {}
468
+ if dg_labelling is not False:
469
+ self.relabel(dg_labelling) # relabelling back
470
+
471
+ self._M = M
472
+ self._M.set_immutable()
473
+ if n + m == 0:
474
+ self._description = 'Quiver without vertices'
475
+ elif n + m == 1:
476
+ self._description = 'Quiver on %d vertex' % (n+m)
477
+ else:
478
+ self._description = 'Quiver on %d vertices' % (n+m)
479
+ self._mutation_type = None
480
+
481
+ # if data is a list of edges, the appropriate digraph is constructed.
482
+
483
+ elif (isinstance(data, (list, EdgesView))
484
+ and all(isinstance(x, (list, tuple)) for x in data)):
485
+ dg = DiGraph(data)
486
+ self.__init__(data=dg, frozen=frozen)
487
+
488
+ # otherwise, an error is raised
489
+ else:
490
+ raise ValueError("the input data was not recognized")
491
+
492
+ # stopgap for bugs arising from coefficients
493
+ if self._m:
494
+ from sage.misc.stopgap import stopgap
495
+ stopgap("Having frozen nodes is known to produce wrong answers", 22381)
496
+
497
+ def __eq__(self, other):
498
+ """
499
+ Return ``True`` if ``self`` and ``other`` represent the same quiver.
500
+
501
+ EXAMPLES::
502
+
503
+ sage: Q = ClusterQuiver(['A',5])
504
+ sage: T = Q.mutate(2, inplace=False)
505
+ sage: Q.__eq__(T)
506
+ False
507
+ sage: T.mutate(2)
508
+ sage: Q.__eq__(T)
509
+ True
510
+ """
511
+ return isinstance(other, ClusterQuiver) and self._M == other._M
512
+
513
+ def __hash__(self) -> int:
514
+ """
515
+ Return a hash of ``self``.
516
+
517
+ EXAMPLES::
518
+
519
+ sage: Q = ClusterQuiver(['A',5])
520
+ sage: hash(Q) # indirect doctest
521
+ 7654921743699262111 # 64-bit
522
+ -1264862561 # 32-bit
523
+ """
524
+ return hash(self._M)
525
+
526
+ def _repr_(self) -> str:
527
+ """
528
+ Return the description of ``self``.
529
+
530
+ EXAMPLES::
531
+
532
+ sage: Q = ClusterQuiver(['A',5])
533
+ sage: Q._repr_()
534
+ "Quiver on 5 vertices of type ['A', 5]"
535
+ """
536
+ name = self._description
537
+ if self._mutation_type:
538
+ if isinstance(self._mutation_type, str):
539
+ name += ' of ' + self._mutation_type
540
+ else:
541
+ name += ' of type ' + str(self._mutation_type)
542
+ if self._m == 1:
543
+ name += ' with %s frozen vertex' % self._m
544
+ elif self._m > 1:
545
+ name += ' with %s frozen vertices' % self._m
546
+ return name
547
+
548
+ def plot(self, circular=True, center=(0, 0), directed=True, mark=None,
549
+ save_pos=False, greens=[]):
550
+ """
551
+ Return the plot of the underlying digraph of ``self``.
552
+
553
+ INPUT:
554
+
555
+ - ``circular`` -- boolean (default: ``True``); if ``True``, the
556
+ circular plot is chosen, otherwise >>spring<< is used
557
+ - ``center`` -- (default: (0,0)) sets the center of the circular plot,
558
+ otherwise it is ignored
559
+ - ``directed`` -- boolean (default: ``True``); if ``True``, the
560
+ directed version is shown, otherwise the undirected
561
+ - ``mark`` -- (default: ``None``) if set to i, the vertex i is
562
+ highlighted
563
+ - ``save_pos`` -- boolean (default: ``False``); if ``True``, the
564
+ positions of the vertices are saved
565
+ - ``greens`` -- (default: ``[]``) if set to a list, will display the green
566
+ vertices as green
567
+
568
+ EXAMPLES::
569
+
570
+ sage: Q = ClusterQuiver(['A',5])
571
+ sage: Q.plot() # needs sage.plot sage.symbolic
572
+ Graphics object consisting of 15 graphics primitives
573
+ sage: Q.plot(circular=True) # needs sage.plot sage.symbolic
574
+ Graphics object consisting of 15 graphics primitives
575
+ sage: Q.plot(circular=True, mark=1) # needs sage.plot sage.symbolic
576
+ Graphics object consisting of 15 graphics primitives
577
+ """
578
+ from sage.plot.colors import rainbow
579
+ from sage.graphs.graph_generators import GraphGenerators
580
+ from sage.symbolic.constants import e, pi
581
+ from sage.rings.cc import CC
582
+ from sage.rings.imaginary_unit import I
583
+ graphs = GraphGenerators()
584
+ # returns positions for graph vertices on two concentric cycles with radius 1 and 2
585
+
586
+ def _graphs_concentric_circles(n, m):
587
+ g1 = graphs.CycleGraph(n).get_pos()
588
+ g2 = graphs.CycleGraph(m).get_pos()
589
+ for i in g2:
590
+ z = CC(g2[i])*e**(-pi*I/(2*m))
591
+ g2[i] = (z.real_part(), z.imag_part())
592
+ for i in range(m):
593
+ g1[n+i] = [2*g2[i][0], 2*g2[i][1]]
594
+ return g1
595
+
596
+ n, m = self._n, self._m
597
+ # So that we don't remove elements of these lists later
598
+ nlist = copy(self._nlist)
599
+ mlist = copy(self._mlist)
600
+ colors = rainbow(11)
601
+ color_dict = {colors[0]: [], colors[1]: [],
602
+ colors[6]: [], colors[5]: []}
603
+
604
+ # Set up our graph. If it's directed we have a digraph, else
605
+ # just a normal graph
606
+ if directed:
607
+ dg = self._digraph.copy(immutable=False)
608
+ else:
609
+ dg = Graph(self._digraph)
610
+
611
+ # For each edge in our graph we assign a color
612
+ for v1, v2, ab in dg.edges(sort=True):
613
+
614
+ if v1 in nlist and v2 in nlist:
615
+ if ab == (1, -1):
616
+ color_dict[colors[0]].append((v1, v2))
617
+ else:
618
+ color_dict[colors[6]].append((v1, v2))
619
+ else:
620
+ if ab == (1, -1):
621
+ color_dict[colors[1]].append((v1, v2))
622
+ else:
623
+ color_dict[colors[5]].append((v1, v2))
624
+ a, b = ab
625
+ if a == -b:
626
+ if a == 1:
627
+ dg.set_edge_label(v1, v2, '')
628
+ else:
629
+ dg.set_edge_label(v1, v2, a)
630
+
631
+ # If a mark is given, then we set that mark apart from the rest
632
+ # The mark is assumed to be a vertex
633
+ if mark is not None:
634
+
635
+ if mark in nlist:
636
+ nlist.remove(mark)
637
+ partition = (nlist, mlist, [mark])
638
+ elif mark in mlist:
639
+ mlist.remove(mark)
640
+ partition = (nlist, mlist, [mark])
641
+ else:
642
+ raise ValueError("the given mark is not a vertex of self")
643
+ else:
644
+
645
+ # Partition out the green vertices
646
+ for i in greens:
647
+ if i in nlist:
648
+ nlist.remove(i)
649
+ else:
650
+ mlist.remove(i)
651
+ partition = (nlist, mlist, greens)
652
+
653
+ vertex_color_dict = {'tomato': partition[0],
654
+ 'lightblue': partition[1],
655
+ 'lightgreen': partition[2]}
656
+
657
+ options = {
658
+ 'graph_border': True,
659
+ 'edge_colors': color_dict,
660
+ 'vertex_colors': vertex_color_dict,
661
+ 'edge_labels': True,
662
+ 'vertex_labels': True,
663
+ }
664
+ if circular:
665
+ pp = _graphs_concentric_circles(n, m)
666
+ options['pos'] = {}
667
+ for v in pp:
668
+ # If we're using vertex dictionary set that as key
669
+ if v in self._vertex_dictionary:
670
+ vkey = self._vertex_dictionary[v]
671
+ else:
672
+ vkey = v
673
+ options['pos'][vkey] = (pp[v][0] + center[0],
674
+ pp[v][1] + center[1])
675
+
676
+ return dg.plot(**options)
677
+
678
+ def show(self, fig_size=1, circular=False, directed=True, mark=None, save_pos=False, greens=[]):
679
+ """
680
+ Show the plot of the underlying digraph of ``self``.
681
+
682
+ INPUT:
683
+
684
+ - ``fig_size`` -- (default: 1) factor by which the size of the plot
685
+ is multiplied
686
+ - ``circular`` -- boolean (default: ``False``); if ``True``, the
687
+ circular plot is chosen, otherwise >>spring<< is used
688
+ - ``directed`` -- boolean (default: ``True``); if ``True``, the directed
689
+ version is shown, otherwise the undirected
690
+ - ``mark`` -- boolean (default: ``None``); if set to i, the vertex i is
691
+ highlighted
692
+ - ``save_pos`` -- boolean (default: ``False``); if ``True``, the
693
+ positions of the vertices are saved
694
+ - ``greens`` -- (default: ``[]``) if set to a list, will display the
695
+ green vertices as green
696
+
697
+ TESTS::
698
+
699
+ sage: Q = ClusterQuiver(['A',5])
700
+ sage: Q.show() # long time
701
+ """
702
+ n, m = self._n, self._m
703
+ plot = self.plot(circular=circular, directed=directed,
704
+ mark=mark, save_pos=save_pos, greens=greens)
705
+ if circular:
706
+ plot.show(figsize=[fig_size*3*(n+m)/4+1, fig_size*3*(n+m)/4+1])
707
+ else:
708
+ plot.show(figsize=[fig_size*n+1, fig_size*n+1])
709
+
710
+ def interact(self, fig_size=1, circular=True):
711
+ r"""
712
+ Start an interactive window for cluster quiver mutations.
713
+
714
+ Only in *Jupyter notebook mode*.
715
+
716
+ INPUT:
717
+
718
+ - ``fig_size`` -- (default: 1) factor by which the size of the
719
+ plot is multiplied
720
+
721
+ - ``circular`` -- boolean (default: ``True``); if ``True``, the
722
+ circular plot is chosen, otherwise >>spring<< is used
723
+
724
+ TESTS::
725
+
726
+ sage: S = ClusterQuiver(['A',4])
727
+ sage: S.interact() # needs sage.plot sage.symbolic
728
+ ...VBox(children=...
729
+ """
730
+ return cluster_interact(self, fig_size, circular, kind='quiver')
731
+
732
+ def save_image(self, filename, circular=False):
733
+ """
734
+ Save the plot of the underlying digraph of ``self``.
735
+
736
+ INPUT:
737
+
738
+ - ``filename`` -- the filename the image is saved to
739
+ - ``circular`` -- boolean (default: ``False``); if ``True``, the
740
+ circular plot is chosen, otherwise >>spring<< is used
741
+
742
+ EXAMPLES::
743
+
744
+ sage: Q = ClusterQuiver(['F',4,[1,2]])
745
+ sage: import tempfile
746
+ sage: with tempfile.NamedTemporaryFile(suffix='.png') as f: # needs sage.plot sage.symbolic
747
+ ....: Q.save_image(f.name)
748
+ """
749
+ graph_plot = self.plot(circular=circular)
750
+ graph_plot.save(filename=filename)
751
+
752
+ def qmu_save(self, filename=None):
753
+ """
754
+ Save ``self`` in a ``.qmu`` file.
755
+
756
+ This file can then be opened in Bernhard Keller's Quiver Applet.
757
+
758
+ See https://webusers.imj-prg.fr/~bernhard.keller/quivermutation/
759
+
760
+ INPUT:
761
+
762
+ - ``filename`` -- the filename the image is saved to
763
+
764
+ If a filename is not specified, the default name is
765
+ ``from_sage.qmu`` in the current sage directory.
766
+
767
+ EXAMPLES::
768
+
769
+ sage: Q = ClusterQuiver(['F',4,[1,2]])
770
+ sage: import tempfile
771
+ sage: with tempfile.NamedTemporaryFile(suffix='.qmu') as f: # needs sage.plot sage.symbolic
772
+ ....: Q.qmu_save(f.name)
773
+
774
+ Make sure we can save quivers with `m != n` frozen variables, see :issue:`14851`::
775
+
776
+ sage: S = ClusterSeed(['A',3])
777
+ sage: T1 = S.principal_extension()
778
+ sage: Q = T1.quiver()
779
+ sage: import tempfile
780
+ sage: with tempfile.NamedTemporaryFile(suffix='.qmu') as f: # needs sage.plot sage.symbolic
781
+ ....: Q.qmu_save(f.name)
782
+ """
783
+ M = self.b_matrix()
784
+ if self.m():
785
+ from sage.matrix.constructor import matrix
786
+ from sage.matrix.constructor import block_matrix
787
+ M1 = M.matrix_from_rows(range(self.n()))
788
+ M2 = M.matrix_from_rows(list(range(self.n(), self.n() + self.m())))
789
+ M3 = matrix(self.m(), self.m())
790
+ M = block_matrix([[M1, -M2.transpose()], [M2, M3]])
791
+ dg = self.digraph()
792
+ dg.plot(save_pos=True)
793
+ PP = dg.get_pos()
794
+ m = M.ncols()
795
+
796
+ if filename is None:
797
+ filename = 'from_sage.qmu'
798
+ try:
799
+ self._default_filename = filename
800
+ except AttributeError:
801
+ pass
802
+ if filename[-4:] != '.qmu':
803
+ filename += '.qmu'
804
+
805
+ string = []
806
+ string.append('//Number of points')
807
+ string.append(str(m))
808
+ string.append('//Vertex radius')
809
+ string.append('9')
810
+ string.append('//Labels shown')
811
+ string.append('1')
812
+ string.append('//Matrix')
813
+ string.append(str(m) + ' ' + str(m))
814
+ string.extend(' '.join(str(M[i, j]) for j in range(m))
815
+ for i in range(m))
816
+ string.append('//Points')
817
+
818
+ for i in range(m):
819
+ x, y = PP[i]
820
+ txt = '9 ' + str(100 * x) + ' ' + str(100 * y)
821
+ if i >= self.n():
822
+ txt += ' 1'
823
+ string.append(txt)
824
+
825
+ string.append('//Historycounter')
826
+ string.append('-1')
827
+ string.append('//History')
828
+ string.append('')
829
+ string.append('//Cluster is null')
830
+
831
+ string = '\n'.join(string)
832
+
833
+ with open(filename, 'w') as myfile:
834
+ myfile.write(string)
835
+
836
+ def b_matrix(self):
837
+ """
838
+ Return the b-matrix of ``self``.
839
+
840
+ EXAMPLES::
841
+
842
+ sage: ClusterQuiver(['A',4]).b_matrix()
843
+ [ 0 1 0 0]
844
+ [-1 0 -1 0]
845
+ [ 0 1 0 1]
846
+ [ 0 0 -1 0]
847
+
848
+ sage: ClusterQuiver(['B',4]).b_matrix()
849
+ [ 0 1 0 0]
850
+ [-1 0 -1 0]
851
+ [ 0 1 0 1]
852
+ [ 0 0 -2 0]
853
+
854
+ sage: ClusterQuiver(['D',4]).b_matrix()
855
+ [ 0 1 0 0]
856
+ [-1 0 -1 -1]
857
+ [ 0 1 0 0]
858
+ [ 0 1 0 0]
859
+
860
+ sage: ClusterQuiver(QuiverMutationType([['A',2],['B',2]])).b_matrix()
861
+ [ 0 1 0 0]
862
+ [-1 0 0 0]
863
+ [ 0 0 0 1]
864
+ [ 0 0 -2 0]
865
+ """
866
+ return copy(self._M)
867
+
868
+ def digraph(self):
869
+ """
870
+ Return the underlying digraph of ``self``.
871
+
872
+ EXAMPLES::
873
+
874
+ sage: ClusterQuiver(['A',1]).digraph()
875
+ Digraph on 1 vertex
876
+ sage: list(ClusterQuiver(['A',1]).digraph())
877
+ [0]
878
+ sage: ClusterQuiver(['A',1]).digraph().edges(sort=True)
879
+ []
880
+
881
+ sage: ClusterQuiver(['A',4]).digraph()
882
+ Digraph on 4 vertices
883
+ sage: ClusterQuiver(['A',4]).digraph().edges(sort=True)
884
+ [(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
885
+
886
+ sage: ClusterQuiver(['B',4]).digraph()
887
+ Digraph on 4 vertices
888
+ sage: ClusterQuiver(['A',4]).digraph().edges(sort=True)
889
+ [(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
890
+
891
+ sage: ClusterQuiver(QuiverMutationType([['A',2],['B',2]])).digraph()
892
+ Digraph on 4 vertices
893
+
894
+ sage: ClusterQuiver(QuiverMutationType([['A',2],['B',2]])).digraph().edges(sort=True)
895
+ [(0, 1, (1, -1)), (2, 3, (1, -2))]
896
+
897
+ sage: ClusterQuiver(['C', 4], user_labels = ['x', 'y', 'z', 'w']).digraph().edges(sort=True)
898
+ [('x', 'y', (1, -1)), ('z', 'w', (2, -1)), ('z', 'y', (1, -1))]
899
+ """
900
+ return self._digraph.copy()
901
+
902
+ def mutation_type(self):
903
+ """
904
+ Return the mutation type of ``self``.
905
+
906
+ Return the mutation_type of each connected component of ``self`` if it
907
+ can be determined, otherwise, the mutation type of this component is
908
+ set to be unknown.
909
+
910
+ The mutation types of the components are ordered by vertex labels.
911
+
912
+ If you do many type recognitions, you should consider to save
913
+ exceptional mutation types using
914
+ :meth:`~sage.combinat.cluster_algebra_quiver.quiver_mutation_type.save_quiver_data`
915
+
916
+ WARNING:
917
+
918
+ - All finite types can be detected,
919
+ - All affine types can be detected, EXCEPT affine type D (the algorithm is not yet implemented)
920
+ - All exceptional types can be detected.
921
+
922
+ EXAMPLES::
923
+
924
+ sage: ClusterQuiver(['A',4]).mutation_type()
925
+ ['A', 4]
926
+ sage: ClusterQuiver(['A',(3,1),1]).mutation_type()
927
+ ['A', [1, 3], 1]
928
+ sage: ClusterQuiver(['C',2]).mutation_type()
929
+ ['B', 2]
930
+ sage: ClusterQuiver(['B',4,1]).mutation_type()
931
+ ['BD', 4, 1]
932
+
933
+ finite types::
934
+
935
+ sage: Q = ClusterQuiver(['A',5])
936
+ sage: Q._mutation_type = None
937
+ sage: Q.mutation_type()
938
+ ['A', 5]
939
+
940
+ sage: Q = ClusterQuiver([(0,1),(1,2),(2,3),(3,4)])
941
+ sage: Q.mutation_type()
942
+ ['A', 5]
943
+
944
+ sage: Q = ClusterQuiver(DiGraph([['a', 'b'], ['c', 'b'], ['c', 'd'], ['e', 'd']]),
945
+ ....: frozen=['c'])
946
+ sage: Q.mutation_type()
947
+ [ ['A', 2], ['A', 2] ]
948
+
949
+ affine types::
950
+
951
+ sage: Q = ClusterQuiver(['E',8,[1,1]]); Q
952
+ Quiver on 10 vertices of type ['E', 8, [1, 1]]
953
+ sage: Q._mutation_type = None; Q
954
+ Quiver on 10 vertices
955
+ sage: Q.mutation_type() # long time
956
+ ['E', 8, [1, 1]]
957
+
958
+ the not yet working affine type D (unless user has saved small classical quiver data)::
959
+
960
+ sage: Q = ClusterQuiver(['D',4,1])
961
+ sage: Q._mutation_type = None
962
+ sage: Q.mutation_type() # todo: not implemented
963
+ ['D', 4, 1]
964
+
965
+ the exceptional types::
966
+
967
+ sage: Q = ClusterQuiver(['X',6])
968
+ sage: Q._mutation_type = None
969
+ sage: Q.mutation_type() # long time
970
+ ['X', 6]
971
+
972
+ examples from page 8 of [Ke2008]_::
973
+
974
+ sage: dg = DiGraph(); dg.add_edges([(9,0),(9,4),(4,6),(6,7),(7,8),(8,3),(3,5),(5,6),(8,1),(2,3)])
975
+ sage: ClusterQuiver(dg).mutation_type() # long time
976
+ ['E', 8, [1, 1]]
977
+
978
+ sage: dg = DiGraph({ 0:[3], 1:[0,4], 2:[0,6], 3:[1,2,7], 4:[3,8], 5:[2], 6:[3,5], 7:[4,6], 8:[7] })
979
+ sage: ClusterQuiver(dg).mutation_type() # long time
980
+ ['E', 8, 1]
981
+
982
+ sage: dg = DiGraph({ 0:[3,9], 1:[0,4], 2:[0,6], 3:[1,2,7], 4:[3,8], 5:[2], 6:[3,5], 7:[4,6], 8:[7], 9:[1] })
983
+ sage: ClusterQuiver(dg).mutation_type() # long time
984
+ ['E', 8, [1, 1]]
985
+
986
+ infinite types::
987
+
988
+ sage: Q = ClusterQuiver(['GR',[4,9]])
989
+ sage: Q._mutation_type = None
990
+ sage: Q.mutation_type()
991
+ 'undetermined infinite mutation type'
992
+
993
+ reducible types::
994
+
995
+ sage: Q = ClusterQuiver([['A', 3], ['B', 3]])
996
+ sage: Q._mutation_type = None
997
+ sage: Q.mutation_type()
998
+ [ ['A', 3], ['B', 3] ]
999
+
1000
+ sage: Q = ClusterQuiver([['A', 3], ['T', [4,4,4]]])
1001
+ sage: Q._mutation_type = None
1002
+ sage: Q.mutation_type()
1003
+ [['A', 3], 'undetermined infinite mutation type']
1004
+
1005
+ sage: Q = ClusterQuiver([['A', 3], ['B', 3], ['T', [4,4,4]]])
1006
+ sage: Q._mutation_type = None
1007
+ sage: Q.mutation_type()
1008
+ [['A', 3], ['B', 3], 'undetermined infinite mutation type']
1009
+
1010
+ sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2],[3,4,1],[4,5,1]])
1011
+ sage: Q.mutation_type()
1012
+ ['undetermined finite mutation type', ['A', 3]]
1013
+
1014
+ TESTS::
1015
+
1016
+ sage: Q = ClusterQuiver(matrix([[0, 3], [-1, 0], [1, 0], [0, 1]]))
1017
+ sage: Q.mutation_type()
1018
+ ['G', 2]
1019
+ sage: Q = ClusterQuiver(matrix([[0, -1, -1, 1, 0], [1, 0, 1, 0, 1], [1, -1, 0, -1, 0], [-1, 0, 1, 0, 1], [0, -1, 0, -1, 0], [0, 1, 0, -1, -1], [0, 1, -1, 0, 0]]))
1020
+ sage: Q.mutation_type()
1021
+ 'undetermined infinite mutation type'
1022
+ """
1023
+ # checking if the mutation type is known already
1024
+ if self._mutation_type is None:
1025
+ # checking mutation type only for the principal part
1026
+ if self._m > 0:
1027
+ dg = self._digraph.subgraph(self._nlist)
1028
+ else:
1029
+ dg = self._digraph
1030
+
1031
+ # checking the type for each connected component
1032
+ mutation_type = []
1033
+ connected_components = sorted(dg.connected_components(sort=False))
1034
+ for component in connected_components:
1035
+ # constructing the digraph for this component
1036
+ dg_component = dg.subgraph(component)
1037
+ dg_component.relabel()
1038
+ # turning dg_component into a canonical form
1039
+ _dg_canonical_form(dg_component)
1040
+ # turning dg_component into a canonical form
1041
+ dig6 = _digraph_to_dig6(dg_component, hashable=True)
1042
+ # and getting the corresponding matrix
1043
+ M = _dig6_to_matrix(dig6)
1044
+
1045
+ # checking if this quiver is mutation infinite
1046
+ is_finite, path = is_mutation_finite(M)
1047
+ if is_finite is False:
1048
+ mut_type_part = 'undetermined infinite mutation type'
1049
+ else:
1050
+ # checking if this quiver is in the database
1051
+ mut_type_part = _mutation_type_from_data(dg_component.order(), dig6, compute_if_necessary=False)
1052
+ # checking if the algorithm can determine the mutation type
1053
+ if mut_type_part == 'unknown':
1054
+ mut_type_part = _connected_mutation_type(dg_component)
1055
+ # checking if this quiver is of exceptional type by computing the exceptional mutation classes
1056
+ if mut_type_part == 'unknown':
1057
+ mut_type_part = _mutation_type_from_data(dg_component.order(), dig6, compute_if_necessary=True)
1058
+ if mut_type_part == 'unknown':
1059
+ mut_type_part = 'undetermined finite mutation type'
1060
+ mutation_type.append(mut_type_part)
1061
+
1062
+ # the empty quiver case
1063
+ if len(mutation_type) == 0:
1064
+ mutation_type = None
1065
+ # the connected quiver case
1066
+ elif len(mutation_type) == 1:
1067
+ mutation_type = mutation_type[0]
1068
+ # the reducible quiver case
1069
+ elif not any(isinstance(mut_type_part, str)
1070
+ for mut_type_part in mutation_type):
1071
+ mutation_type = QuiverMutationType(mutation_type)
1072
+ self._mutation_type = mutation_type
1073
+ return self._mutation_type
1074
+
1075
+ def n(self):
1076
+ """
1077
+ Return the number of free vertices of ``self``.
1078
+
1079
+ EXAMPLES::
1080
+
1081
+ sage: ClusterQuiver(['A',4]).n()
1082
+ 4
1083
+ sage: ClusterQuiver(['A',(3,1),1]).n()
1084
+ 4
1085
+ sage: ClusterQuiver(['B',4]).n()
1086
+ 4
1087
+ sage: ClusterQuiver(['B',4,1]).n()
1088
+ 5
1089
+ """
1090
+ return self._n
1091
+
1092
+ def m(self):
1093
+ """
1094
+ Return the number of frozen vertices of ``self``.
1095
+
1096
+ EXAMPLES::
1097
+
1098
+ sage: Q = ClusterQuiver(['A',4])
1099
+ sage: Q.m()
1100
+ 0
1101
+
1102
+ sage: T = ClusterQuiver(Q.digraph().edges(sort=True), frozen=[3])
1103
+ sage: T.n()
1104
+ 3
1105
+ sage: T.m()
1106
+ 1
1107
+ """
1108
+ return self._m
1109
+
1110
+ def free_vertices(self) -> list:
1111
+ """
1112
+ Return the list of free vertices of ``self``.
1113
+
1114
+ EXAMPLES::
1115
+
1116
+ sage: Q = ClusterQuiver(DiGraph([['a', 'b'], ['c', 'b'], ['c', 'd'], ['e', 'd']]),
1117
+ ....: frozen=['b', 'd'])
1118
+ sage: Q.free_vertices()
1119
+ ['a', 'c', 'e']
1120
+ """
1121
+ return self._nlist
1122
+
1123
+ def frozen_vertices(self) -> list:
1124
+ """
1125
+ Return the list of frozen vertices of ``self``.
1126
+
1127
+ EXAMPLES::
1128
+
1129
+ sage: Q = ClusterQuiver(DiGraph([['a', 'b'], ['c', 'b'], ['c', 'd'], ['e', 'd']]),
1130
+ ....: frozen=['b', 'd'])
1131
+ sage: sorted(Q.frozen_vertices())
1132
+ ['b', 'd']
1133
+ """
1134
+ return self._mlist
1135
+
1136
+ def canonical_label(self, certificate=False):
1137
+ """
1138
+ Return the canonical labelling of ``self``.
1139
+
1140
+ See :meth:`sage.graphs.generic_graph.GenericGraph.canonical_label`.
1141
+
1142
+ INPUT:
1143
+
1144
+ - ``certificate`` -- boolean (default: ``False``); if ``True``, the
1145
+ dictionary from ``self.vertices()`` to the vertices of the returned
1146
+ quiver is returned as well
1147
+
1148
+ EXAMPLES::
1149
+
1150
+ sage: Q = ClusterQuiver(['A',4]); Q.digraph().edges(sort=True)
1151
+ [(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
1152
+
1153
+ sage: T = Q.canonical_label(); T.digraph().edges(sort=True)
1154
+ [(0, 3, (1, -1)), (1, 2, (1, -1)), (1, 3, (1, -1))]
1155
+
1156
+ sage: T, iso = Q.canonical_label(certificate=True)
1157
+ sage: T.digraph().edges(sort=True); iso
1158
+ [(0, 3, (1, -1)), (1, 2, (1, -1)), (1, 3, (1, -1))]
1159
+ {0: 0, 1: 3, 2: 1, 3: 2}
1160
+
1161
+ sage: Q = ClusterQuiver(QuiverMutationType([['B',2],['A',1]])); Q
1162
+ Quiver on 3 vertices of type [ ['B', 2], ['A', 1] ]
1163
+
1164
+ sage: Q.canonical_label()
1165
+ Quiver on 3 vertices of type [ ['A', 1], ['B', 2] ]
1166
+
1167
+ sage: Q.canonical_label(certificate=True)
1168
+ (Quiver on 3 vertices of type [ ['A', 1], ['B', 2] ], {0: 1, 1: 2, 2: 0})
1169
+ """
1170
+ # computing the canonical form respecting the frozen variables
1171
+ dg = self._digraph.copy()
1172
+ iso, _ = _dg_canonical_form(dg, frozen=self._mlist)
1173
+ frozen = [iso[i] for i in self._mlist]
1174
+ Q = ClusterQuiver(dg, frozen=frozen)
1175
+ # getting the new ordering for the mutation type if necessary
1176
+ if self._mutation_type:
1177
+ if dg.is_connected():
1178
+ Q._mutation_type = self._mutation_type
1179
+ else:
1180
+ CC = sorted(self._digraph.connected_components(sort=False))
1181
+ CC_new = sorted(zip([sorted(iso[i] for i in L) for L in CC],
1182
+ range(len(CC))))
1183
+ comp_iso = [L[1] for L in CC_new]
1184
+ Q._mutation_type = [copy(self._mutation_type.irreducible_components()[comp_i])
1185
+ for comp_i in comp_iso]
1186
+ Q._mutation_type = QuiverMutationType(Q._mutation_type)
1187
+ if certificate:
1188
+ return Q, iso
1189
+ else:
1190
+ return Q
1191
+
1192
+ def is_acyclic(self) -> bool:
1193
+ """
1194
+ Return true if ``self`` is acyclic.
1195
+
1196
+ EXAMPLES::
1197
+
1198
+ sage: ClusterQuiver(['A',4]).is_acyclic()
1199
+ True
1200
+
1201
+ sage: ClusterQuiver(['A',[2,1],1]).is_acyclic()
1202
+ True
1203
+
1204
+ sage: ClusterQuiver([[0,1],[1,2],[2,0]]).is_acyclic()
1205
+ False
1206
+ """
1207
+ return self._digraph.is_directed_acyclic()
1208
+
1209
+ def is_bipartite(self, return_bipartition=False):
1210
+ """
1211
+ Return ``True`` if ``self`` is bipartite.
1212
+
1213
+ EXAMPLES::
1214
+
1215
+ sage: ClusterQuiver(['A',[3,3],1]).is_bipartite()
1216
+ True
1217
+
1218
+ sage: ClusterQuiver(['A',[4,3],1]).is_bipartite()
1219
+ False
1220
+ """
1221
+ dg = self._digraph.copy(immutable=False)
1222
+ dg.delete_vertices(range(self._n, self._n + self._m))
1223
+ if any(dg.in_degree(i) and dg.out_degree(i) for i in dg):
1224
+ return False
1225
+ if not return_bipartition:
1226
+ return True
1227
+ return dg.to_undirected().bipartite_sets()
1228
+
1229
+ def exchangeable_part(self):
1230
+ """
1231
+ Return the restriction to the principal part (i.e. exchangeable part) of ``self``, the subquiver obtained by deleting the frozen vertices of ``self``.
1232
+
1233
+ EXAMPLES::
1234
+
1235
+ sage: Q = ClusterQuiver(['A',4])
1236
+ sage: T = ClusterQuiver(Q.digraph().edges(sort=True), frozen=[3])
1237
+ sage: T.digraph().edges(sort=True)
1238
+ [(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
1239
+
1240
+ sage: T.exchangeable_part().digraph().edges(sort=True)
1241
+ [(0, 1, (1, -1)), (2, 1, (1, -1))]
1242
+
1243
+ sage: Q2 = Q.principal_extension()
1244
+ sage: Q3 = Q2.principal_extension()
1245
+ sage: Q2.exchangeable_part() == Q3.exchangeable_part()
1246
+ True
1247
+ """
1248
+ dg = self._digraph.copy(immutable=False)
1249
+ dg.delete_vertices(range(self._n, self._n + self._m))
1250
+ Q = ClusterQuiver(dg)
1251
+ Q._mutation_type = self._mutation_type
1252
+ return Q
1253
+
1254
+ def principal_extension(self, inplace=False):
1255
+ """
1256
+ Return the principal extension of ``self``, adding n frozen vertices
1257
+ to any previously frozen vertices.
1258
+
1259
+ This is the quiver obtained by adding an outgoing edge to
1260
+ every mutable vertex of ``self``.
1261
+
1262
+ EXAMPLES::
1263
+
1264
+ sage: Q = ClusterQuiver(['A',2]); Q
1265
+ Quiver on 2 vertices of type ['A', 2]
1266
+ sage: T = Q.principal_extension(); T
1267
+ Quiver on 4 vertices of type ['A', 2] with 2 frozen vertices
1268
+ sage: T2 = T.principal_extension(); T2
1269
+ Quiver on 6 vertices of type ['A', 2] with 4 frozen vertices
1270
+ sage: Q.digraph().edges(sort=True)
1271
+ [(0, 1, (1, -1))]
1272
+ sage: T.digraph().edges(sort=True)
1273
+ [(0, 1, (1, -1)), (2, 0, (1, -1)), (3, 1, (1, -1))]
1274
+ sage: T2.digraph().edges(sort=True)
1275
+ [(0, 1, (1, -1)), (2, 0, (1, -1)), (3, 1, (1, -1)),
1276
+ (4, 0, (1, -1)), (5, 1, (1, -1))]
1277
+ """
1278
+ dg = self._digraph.copy(immutable=False)
1279
+ dg.add_edges([(self._n + self._m + i, i) for i in range(self._n)])
1280
+ Q = ClusterQuiver(dg, frozen=list(range(self._n,
1281
+ 2 * self._n + self._m)))
1282
+ Q._mutation_type = self._mutation_type
1283
+ if inplace:
1284
+ self.__init__(Q)
1285
+ else:
1286
+ return Q
1287
+
1288
+ def first_sink(self):
1289
+ r"""
1290
+ Return the first vertex of ``self`` that is a sink.
1291
+
1292
+ EXAMPLES::
1293
+
1294
+ sage: Q = ClusterQuiver(['A',5])
1295
+ sage: Q.mutate([1,2,4,3,2])
1296
+ sage: Q.first_sink()
1297
+ 0
1298
+ """
1299
+ sinks = self.digraph().sinks()
1300
+
1301
+ if sinks:
1302
+ return sinks[0]
1303
+ return None
1304
+
1305
+ def sinks(self) -> list:
1306
+ r"""
1307
+ Return all vertices of ``self`` that are sinks.
1308
+
1309
+ EXAMPLES::
1310
+
1311
+ sage: Q = ClusterQuiver(['A',5])
1312
+ sage: Q.mutate([1,2,4,3,2])
1313
+ sage: Q.sinks()
1314
+ [0, 2]
1315
+
1316
+ sage: Q = ClusterQuiver(['A',5])
1317
+ sage: Q.mutate([2,1,3,4,2])
1318
+ sage: Q.sinks()
1319
+ [3]
1320
+ """
1321
+ return self.digraph().sinks()
1322
+
1323
+ def first_source(self):
1324
+ r"""
1325
+ Return the first vertex of ``self`` that is a source.
1326
+
1327
+ EXAMPLES::
1328
+
1329
+ sage: Q = ClusterQuiver(['A',5])
1330
+ sage: Q.mutate([2,1,3,4,2])
1331
+ sage: Q.first_source()
1332
+ 1
1333
+ """
1334
+ sources = self.digraph().sources()
1335
+
1336
+ if sources:
1337
+ return sources[0]
1338
+ return None
1339
+
1340
+ def sources(self) -> list:
1341
+ r"""
1342
+ Return all vertices of ``self`` that are sources.
1343
+
1344
+ EXAMPLES::
1345
+
1346
+ sage: Q = ClusterQuiver(['A',5])
1347
+ sage: Q.mutate([1,2,4,3,2])
1348
+ sage: Q.sources()
1349
+ []
1350
+
1351
+ sage: Q = ClusterQuiver(['A',5])
1352
+ sage: Q.mutate([2,1,3,4,2])
1353
+ sage: Q.sources()
1354
+ [1]
1355
+ """
1356
+ return self.digraph().sources()
1357
+
1358
+ def mutate(self, data, inplace=True):
1359
+ """
1360
+ Mutate ``self`` at a sequence of vertices.
1361
+
1362
+ INPUT:
1363
+
1364
+ - ``sequence`` -- a vertex of ``self``, an iterator of vertices of
1365
+ ``self``, a function which takes in the ClusterQuiver and returns a
1366
+ vertex or an iterator of vertices, or a string of the parameter
1367
+ wanting to be called on ``ClusterQuiver`` that will return a vertex
1368
+ or an iterator of vertices
1369
+ - ``inplace`` -- boolean (default: ``True``); if ``False``, the result
1370
+ is returned, otherwise ``self`` is modified
1371
+
1372
+ EXAMPLES::
1373
+
1374
+ sage: Q = ClusterQuiver(['A',4]); Q.b_matrix()
1375
+ [ 0 1 0 0]
1376
+ [-1 0 -1 0]
1377
+ [ 0 1 0 1]
1378
+ [ 0 0 -1 0]
1379
+
1380
+ sage: Q.mutate(0); Q.b_matrix()
1381
+ [ 0 -1 0 0]
1382
+ [ 1 0 -1 0]
1383
+ [ 0 1 0 1]
1384
+ [ 0 0 -1 0]
1385
+
1386
+ sage: T = Q.mutate(0, inplace=False); T
1387
+ Quiver on 4 vertices of type ['A', 4]
1388
+
1389
+ sage: Q.mutate(0)
1390
+ sage: Q == T
1391
+ True
1392
+
1393
+ sage: Q.mutate([0,1,0])
1394
+ sage: Q.b_matrix()
1395
+ [ 0 -1 1 0]
1396
+ [ 1 0 0 0]
1397
+ [-1 0 0 1]
1398
+ [ 0 0 -1 0]
1399
+
1400
+ sage: Q = ClusterQuiver(QuiverMutationType([['A',1],['A',3]]))
1401
+ sage: Q.b_matrix()
1402
+ [ 0 0 0 0]
1403
+ [ 0 0 1 0]
1404
+ [ 0 -1 0 -1]
1405
+ [ 0 0 1 0]
1406
+
1407
+ sage: T = Q.mutate(0,inplace=False)
1408
+ sage: Q == T
1409
+ True
1410
+
1411
+ sage: Q = ClusterQuiver(['A',3]); Q.b_matrix()
1412
+ [ 0 1 0]
1413
+ [-1 0 -1]
1414
+ [ 0 1 0]
1415
+ sage: Q.mutate('first_sink'); Q.b_matrix()
1416
+ [ 0 -1 0]
1417
+ [ 1 0 1]
1418
+ [ 0 -1 0]
1419
+ sage: Q.mutate('first_source'); Q.b_matrix()
1420
+ [ 0 1 0]
1421
+ [-1 0 -1]
1422
+ [ 0 1 0]
1423
+
1424
+ sage: dg = DiGraph()
1425
+ sage: dg.add_vertices(['a','b','c','d','e'])
1426
+ sage: dg.add_edges([['a','b'], ['b','c'], ['c','d'], ['d','e']])
1427
+ sage: Q2 = ClusterQuiver(dg, frozen=['c']); Q2.b_matrix()
1428
+ [ 0 1 0 0]
1429
+ [-1 0 0 0]
1430
+ [ 0 0 0 1]
1431
+ [ 0 0 -1 0]
1432
+ [ 0 -1 1 0]
1433
+ sage: Q2.mutate('a'); Q2.b_matrix()
1434
+ [ 0 -1 0 0]
1435
+ [ 1 0 0 0]
1436
+ [ 0 0 0 1]
1437
+ [ 0 0 -1 0]
1438
+ [ 0 -1 1 0]
1439
+
1440
+ sage: dg = DiGraph([['a', 'b'], ['b', 'c']], format='list_of_edges')
1441
+ sage: Q = ClusterQuiver(dg);Q
1442
+ Quiver on 3 vertices
1443
+ sage: Q.mutate(['a','b'],inplace=False).digraph().edges(sort=True)
1444
+ [('a', 'b', (1, -1)), ('c', 'b', (1, -1))]
1445
+
1446
+ TESTS::
1447
+
1448
+ sage: Q = ClusterQuiver(['A',4]); Q.mutate(0,1)
1449
+ Traceback (most recent call last):
1450
+ ...
1451
+ ValueError: The second parameter must be boolean. To mutate at a sequence of length 2, input it as a list.
1452
+
1453
+ sage: Q = ClusterQuiver(['A',4]); Q.mutate(0,0)
1454
+ Traceback (most recent call last):
1455
+ ...
1456
+ ValueError: The second parameter must be boolean. To mutate at a sequence of length 2, input it as a list.
1457
+ """
1458
+ dg = self._digraph
1459
+ V = nlist = self._nlist
1460
+ mlist = self._mlist
1461
+
1462
+ # If we get a string which is not a cluster variable, execute as a function
1463
+ if isinstance(data, str):
1464
+ if data not in V:
1465
+ data = getattr(self, data)()
1466
+
1467
+ # If we get a function, execute it
1468
+ if callable(data):
1469
+ # function should return either integer or sequence
1470
+ data = data(self)
1471
+
1472
+ if data is None:
1473
+ raise ValueError('Not mutating: No vertices given.')
1474
+
1475
+ if data in V:
1476
+ seq = [data]
1477
+ else:
1478
+ seq = data
1479
+ if isinstance(seq, tuple):
1480
+ seq = list(seq)
1481
+ if not isinstance(seq, list):
1482
+ raise ValueError('The quiver can only be mutated at a vertex or at a sequence of vertices')
1483
+ if not isinstance(inplace, bool):
1484
+ raise ValueError('The second parameter must be boolean. To mutate at a sequence of length 2, input it as a list.')
1485
+ if any(v not in V for v in seq):
1486
+ v = next(v for v in seq if v not in V)
1487
+ raise ValueError('The quiver cannot be mutated at the vertex %s' % v)
1488
+
1489
+ for v in seq:
1490
+ dg = _digraph_mutate(dg, v, frozen=mlist)
1491
+
1492
+ if inplace:
1493
+ self._M = _edge_list_to_matrix(dg.edge_iterator(), nlist, mlist)
1494
+ self._M.set_immutable()
1495
+ self._digraph = dg
1496
+ else:
1497
+ Q = ClusterQuiver(dg, frozen=mlist)
1498
+ Q._mutation_type = self._mutation_type
1499
+ return Q
1500
+
1501
+ def mutation_sequence(self, sequence, show_sequence=False, fig_size=1.2):
1502
+ """
1503
+ Return a list containing the sequence of quivers obtained from ``self``
1504
+ by a sequence of mutations on vertices.
1505
+
1506
+ INPUT:
1507
+
1508
+ - ``sequence`` -- list or tuple of vertices of ``self``
1509
+ - ``show_sequence`` -- boolean (default: ``False``); if ``True``, a png
1510
+ containing the mutation sequence is shown
1511
+ - ``fig_size`` -- (default: 1.2) factor by which the size of the
1512
+ sequence is expanded
1513
+
1514
+ EXAMPLES::
1515
+
1516
+ sage: Q = ClusterQuiver(['A',4])
1517
+ sage: seq = Q.mutation_sequence([0,1]); seq
1518
+ [Quiver on 4 vertices of type ['A', 4],
1519
+ Quiver on 4 vertices of type ['A', 4],
1520
+ Quiver on 4 vertices of type ['A', 4]]
1521
+ sage: [T.b_matrix() for T in seq]
1522
+ [
1523
+ [ 0 1 0 0] [ 0 -1 0 0] [ 0 1 -1 0]
1524
+ [-1 0 -1 0] [ 1 0 -1 0] [-1 0 1 0]
1525
+ [ 0 1 0 1] [ 0 1 0 1] [ 1 -1 0 1]
1526
+ [ 0 0 -1 0], [ 0 0 -1 0], [ 0 0 -1 0]
1527
+ ]
1528
+ """
1529
+ n = self._n
1530
+ m = self._m
1531
+ if m == 0:
1532
+ width_factor = 3
1533
+ fig_size = fig_size*2*n/3
1534
+ else:
1535
+ width_factor = 6
1536
+ fig_size = fig_size*4*n/3
1537
+ V = range(n)
1538
+
1539
+ if isinstance(sequence, tuple):
1540
+ sequence = list(sequence)
1541
+ if not isinstance(sequence, list):
1542
+ raise ValueError('the quiver can only be mutated at a vertex'
1543
+ ' or at a sequence of vertices')
1544
+ if any(v not in V for v in sequence):
1545
+ v = next(v for v in sequence if v not in V)
1546
+ raise ValueError(f'the quiver can only be mutated at the vertex {v}')
1547
+
1548
+ quiver = copy(self)
1549
+ quiver_sequence = []
1550
+ quiver_sequence.append(copy(quiver))
1551
+
1552
+ for v in sequence:
1553
+ quiver.mutate(v)
1554
+ quiver_sequence.append(copy(quiver))
1555
+
1556
+ if show_sequence:
1557
+ from sage.plot.plot import Graphics
1558
+ from sage.plot.text import text
1559
+
1560
+ def _plot_arrow(v, k, center=(0, 0)):
1561
+ return text(r"$\longleftrightarrow$", (center[0], center[1]), fontsize=25) + text(r"$\mu_"+str(v)+"$", (center[0], center[1]+0.15), fontsize=15) \
1562
+ + text("$"+str(k)+"$", (center[0], center[1]-0.2), fontsize=15)
1563
+
1564
+ plot_sequence = [quiver_sequence[i].plot(circular=True, center=(i*width_factor, 0)) for i in range(len(quiver_sequence))]
1565
+ arrow_sequence = [_plot_arrow(sequence[i], i+1, center=((i+0.5)*width_factor, 0)) for i in range(len(sequence))]
1566
+ sequence = []
1567
+ for i in range(len(plot_sequence)):
1568
+ if i < len(arrow_sequence):
1569
+ sequence.append(plot_sequence[i] + arrow_sequence[i])
1570
+ else:
1571
+ sequence.append(plot_sequence[i])
1572
+ plot_obj = Graphics()
1573
+ for elem in sequence:
1574
+ plot_obj += elem
1575
+ plot_obj.show(axes=False, figsize=[fig_size*len(quiver_sequence),
1576
+ fig_size])
1577
+ return quiver_sequence
1578
+
1579
+ def reorient(self, data):
1580
+ """
1581
+ Reorient ``self`` with respect to the given total order, or
1582
+ with respect to an iterator of edges in ``self`` to be
1583
+ reverted.
1584
+
1585
+ .. WARNING::
1586
+
1587
+ This operation might change the mutation type of ``self``.
1588
+
1589
+ INPUT:
1590
+
1591
+ - ``data`` -- an iterator defining a total order on
1592
+ ``self.vertices()``, or an iterator of edges in ``self`` to
1593
+ be reoriented.
1594
+
1595
+ EXAMPLES::
1596
+
1597
+ sage: Q = ClusterQuiver(['A',(2,3),1])
1598
+ sage: Q.mutation_type()
1599
+ ['A', [2, 3], 1]
1600
+
1601
+ sage: Q.reorient([(0,1),(1,2),(2,3),(3,4)])
1602
+ sage: Q.mutation_type()
1603
+ ['D', 5]
1604
+
1605
+ sage: Q.reorient([0,1,2,3,4])
1606
+ sage: Q.mutation_type()
1607
+ ['A', [1, 4], 1]
1608
+
1609
+ TESTS::
1610
+
1611
+ sage: Q = ClusterQuiver(['A',2])
1612
+ sage: Q.reorient([])
1613
+ Traceback (most recent call last):
1614
+ ...
1615
+ ValueError: empty input
1616
+ sage: Q.reorient([3,4])
1617
+ Traceback (most recent call last):
1618
+ ...
1619
+ ValueError: not a total order on the vertices of the quiver or
1620
+ a list of edges to be oriented
1621
+ """
1622
+ if not data:
1623
+ raise ValueError('empty input')
1624
+ first = data[0]
1625
+
1626
+ if set(data) == set(range(self._n + self._m)):
1627
+ dg_new = DiGraph()
1628
+ for edge in self._digraph.edges(sort=True):
1629
+ if data.index(edge[0]) < data.index(edge[1]):
1630
+ dg_new.add_edge(edge[0], edge[1], edge[2])
1631
+ else:
1632
+ dg_new.add_edge(edge[1], edge[0], edge[2])
1633
+ self._digraph = dg_new
1634
+ self._M = _edge_list_to_matrix(dg_new.edges(sort=True),
1635
+ self._nlist, self._mlist)
1636
+ self._M.set_immutable()
1637
+ self._mutation_type = None
1638
+ elif isinstance(first, (list, tuple)) and len(first) == 2:
1639
+ edges = self._digraph.edges(sort=True, labels=False)
1640
+ for edge in data:
1641
+ if (edge[1], edge[0]) in edges:
1642
+ label = self._digraph.edge_label(edge[1], edge[0])
1643
+ self._digraph.delete_edge(edge[1], edge[0])
1644
+ self._digraph.add_edge(edge[0], edge[1], label)
1645
+ self._M = _edge_list_to_matrix(self._digraph.edges(sort=True),
1646
+ self._nlist, self._mlist)
1647
+ self._M.set_immutable()
1648
+ self._mutation_type = None
1649
+ else:
1650
+ raise ValueError('not a total order on the vertices of the quiver'
1651
+ ' or a list of edges to be oriented')
1652
+
1653
+ def mutation_class_iter(self, depth=infinity, show_depth=False,
1654
+ return_paths=False, data_type='quiver',
1655
+ up_to_equivalence=True, sink_source=False):
1656
+ """
1657
+ Return an iterator for the mutation class of ``self``
1658
+ together with certain constraints.
1659
+
1660
+ INPUT:
1661
+
1662
+ - ``depth`` -- integer (default: infinity); only quivers with distance
1663
+ at most depth from ``self`` are returned.
1664
+ - ``show_depth`` -- boolean (default: ``False``); if ``True``, the
1665
+ actual depth of the mutation is shown
1666
+ - ``return_paths`` -- boolean (default: ``False``); if ``True``, a
1667
+ shortest path of mutation sequences from ``self`` to the given quiver
1668
+ is returned as well
1669
+ - ``data_type`` -- (default: ``'quiver'``) can be one of the following::
1670
+
1671
+ * "quiver"
1672
+ * "matrix"
1673
+ * "digraph"
1674
+ * "dig6"
1675
+ * "path"
1676
+
1677
+ - ``up_to_equivalence`` -- boolean (default: ``True``); if ``True``,
1678
+ only one quiver for each graph-isomorphism class is recorded
1679
+ - ``sink_source`` -- boolean (default: ``False``); if ``True``, only
1680
+ mutations at sinks and sources are applied
1681
+
1682
+ EXAMPLES::
1683
+
1684
+ sage: Q = ClusterQuiver(['A',3])
1685
+ sage: it = Q.mutation_class_iter()
1686
+ sage: for T in it: print(T)
1687
+ Quiver on 3 vertices of type ['A', 3]
1688
+ Quiver on 3 vertices of type ['A', 3]
1689
+ Quiver on 3 vertices of type ['A', 3]
1690
+ Quiver on 3 vertices of type ['A', 3]
1691
+
1692
+ sage: it = Q.mutation_class_iter(depth=1)
1693
+ sage: for T in it: print(T)
1694
+ Quiver on 3 vertices of type ['A', 3]
1695
+ Quiver on 3 vertices of type ['A', 3]
1696
+ Quiver on 3 vertices of type ['A', 3]
1697
+
1698
+ sage: it = Q.mutation_class_iter(show_depth=True)
1699
+ sage: for T in it: pass
1700
+ Depth: 0 found: 1 Time: ... s
1701
+ Depth: 1 found: 3 Time: ... s
1702
+ Depth: 2 found: 4 Time: ... s
1703
+
1704
+ sage: it = Q.mutation_class_iter(return_paths=True)
1705
+ sage: for T in it: print(T)
1706
+ (Quiver on 3 vertices of type ['A', 3], [])
1707
+ (Quiver on 3 vertices of type ['A', 3], [1])
1708
+ (Quiver on 3 vertices of type ['A', 3], [0])
1709
+ (Quiver on 3 vertices of type ['A', 3], [0, 1])
1710
+
1711
+ sage: it = Q.mutation_class_iter(up_to_equivalence=False)
1712
+ sage: for T in it: print(T)
1713
+ Quiver on 3 vertices of type ['A', 3]
1714
+ Quiver on 3 vertices of type ['A', 3]
1715
+ Quiver on 3 vertices of type ['A', 3]
1716
+ Quiver on 3 vertices of type ['A', 3]
1717
+ Quiver on 3 vertices of type ['A', 3]
1718
+ Quiver on 3 vertices of type ['A', 3]
1719
+ Quiver on 3 vertices of type ['A', 3]
1720
+ Quiver on 3 vertices of type ['A', 3]
1721
+ Quiver on 3 vertices of type ['A', 3]
1722
+ Quiver on 3 vertices of type ['A', 3]
1723
+ Quiver on 3 vertices of type ['A', 3]
1724
+ Quiver on 3 vertices of type ['A', 3]
1725
+ Quiver on 3 vertices of type ['A', 3]
1726
+ Quiver on 3 vertices of type ['A', 3]
1727
+
1728
+ sage: it = Q.mutation_class_iter(return_paths=True,
1729
+ ....: up_to_equivalence=False)
1730
+ sage: mutation_class = list(it)
1731
+ sage: len(mutation_class)
1732
+ 14
1733
+ sage: mutation_class[0]
1734
+ (Quiver on 3 vertices of type ['A', 3], [])
1735
+
1736
+ sage: Q = ClusterQuiver(['A',3])
1737
+ sage: it = Q.mutation_class_iter(data_type='path')
1738
+ sage: for T in it: print(T)
1739
+ []
1740
+ [1]
1741
+ [0]
1742
+ [0, 1]
1743
+
1744
+ sage: Q = ClusterQuiver(['A',3])
1745
+ sage: it = Q.mutation_class_iter(return_paths=True,
1746
+ ....: data_type='matrix')
1747
+ sage: next(it)
1748
+ (
1749
+ [ 0 0 1]
1750
+ [ 0 0 1]
1751
+ [-1 -1 0], []
1752
+ )
1753
+
1754
+ sage: dg = DiGraph([['a', 'b'], ['b', 'c']],
1755
+ ....: format='list_of_edges')
1756
+ sage: S = ClusterQuiver(dg, frozen=['b'])
1757
+ sage: S.mutation_class()
1758
+ [Quiver on 3 vertices with 1 frozen vertex,
1759
+ Quiver on 3 vertices with 1 frozen vertex,
1760
+ Quiver on 3 vertices with 1 frozen vertex]
1761
+ """
1762
+ if data_type == 'path':
1763
+ return_paths = False
1764
+ if data_type == "dig6":
1765
+ return_dig6 = True
1766
+ else:
1767
+ return_dig6 = False
1768
+
1769
+ # jump to the standard labelling convention
1770
+ dg = ClusterQuiver(self._M).digraph()
1771
+ frozen = list(range(self._n, self._n + self._m))
1772
+
1773
+ MC_iter = _mutation_class_iter(dg, self._n, self._m, depth=depth,
1774
+ return_dig6=return_dig6,
1775
+ show_depth=show_depth,
1776
+ up_to_equivalence=up_to_equivalence,
1777
+ sink_source=sink_source)
1778
+ for data in MC_iter:
1779
+ if data_type == "quiver":
1780
+ next_element = ClusterQuiver(data[0], frozen=frozen)
1781
+ next_element._mutation_type = self._mutation_type
1782
+ elif data_type == "matrix":
1783
+ next_element = ClusterQuiver(data[0], frozen=frozen)._M
1784
+ elif data_type == "digraph":
1785
+ next_element = data[0]
1786
+ elif data_type == "dig6":
1787
+ next_element = data[0]
1788
+ elif data_type == "path":
1789
+ next_element = data[1]
1790
+ else:
1791
+ raise ValueError("the parameter for data_type was "
1792
+ "not recognized")
1793
+ if return_paths:
1794
+ yield (next_element, data[1])
1795
+ else:
1796
+ yield next_element
1797
+
1798
+ def mutation_class(self, depth=infinity, show_depth=False, return_paths=False,
1799
+ data_type='quiver', up_to_equivalence=True, sink_source=False):
1800
+ """
1801
+ Return the mutation class of ``self`` together with certain constraints.
1802
+
1803
+ INPUT:
1804
+
1805
+ - ``depth`` -- (default: ``infinity``) integer, only seeds with
1806
+ distance at most depth from ``self`` are returned
1807
+ - ``show_depth`` -- boolean (default: ``False``); if ``True``, the
1808
+ actual depth of the mutation is shown
1809
+ - ``return_paths`` -- boolean (default: ``False``); if ``True``, a
1810
+ shortest path of mutation sequences from ``self`` to the given
1811
+ quiver is returned as well
1812
+ - ``data_type`` -- (default: ``'quiver'``) can be one of
1813
+ the following:
1814
+
1815
+ * ``'quiver'`` -- the quiver is returned
1816
+ * ``'dig6'`` -- the dig6-data is returned
1817
+ * ``'path'`` -- shortest paths of mutation sequences from
1818
+ ``self`` are returned
1819
+
1820
+ - ``sink_source`` -- boolean (default: ``False``); if ``True``, only
1821
+ mutations at sinks and sources are applied
1822
+
1823
+ EXAMPLES::
1824
+
1825
+ sage: Q = ClusterQuiver(['A',3])
1826
+ sage: Ts = Q.mutation_class()
1827
+ sage: for T in Ts: print(T)
1828
+ Quiver on 3 vertices of type ['A', 3]
1829
+ Quiver on 3 vertices of type ['A', 3]
1830
+ Quiver on 3 vertices of type ['A', 3]
1831
+ Quiver on 3 vertices of type ['A', 3]
1832
+
1833
+ sage: Ts = Q.mutation_class(depth=1)
1834
+ sage: for T in Ts: print(T)
1835
+ Quiver on 3 vertices of type ['A', 3]
1836
+ Quiver on 3 vertices of type ['A', 3]
1837
+ Quiver on 3 vertices of type ['A', 3]
1838
+
1839
+ sage: Ts = Q.mutation_class(show_depth=True)
1840
+ Depth: 0 found: 1 Time: ... s
1841
+ Depth: 1 found: 3 Time: ... s
1842
+ Depth: 2 found: 4 Time: ... s
1843
+
1844
+ sage: Ts = Q.mutation_class(return_paths=True)
1845
+ sage: for T in Ts: print(T)
1846
+ (Quiver on 3 vertices of type ['A', 3], [])
1847
+ (Quiver on 3 vertices of type ['A', 3], [1])
1848
+ (Quiver on 3 vertices of type ['A', 3], [0])
1849
+ (Quiver on 3 vertices of type ['A', 3], [0, 1])
1850
+
1851
+ sage: Ts = Q.mutation_class(up_to_equivalence=False)
1852
+ sage: for T in Ts: print(T)
1853
+ Quiver on 3 vertices of type ['A', 3]
1854
+ Quiver on 3 vertices of type ['A', 3]
1855
+ Quiver on 3 vertices of type ['A', 3]
1856
+ Quiver on 3 vertices of type ['A', 3]
1857
+ Quiver on 3 vertices of type ['A', 3]
1858
+ Quiver on 3 vertices of type ['A', 3]
1859
+ Quiver on 3 vertices of type ['A', 3]
1860
+ Quiver on 3 vertices of type ['A', 3]
1861
+ Quiver on 3 vertices of type ['A', 3]
1862
+ Quiver on 3 vertices of type ['A', 3]
1863
+ Quiver on 3 vertices of type ['A', 3]
1864
+ Quiver on 3 vertices of type ['A', 3]
1865
+ Quiver on 3 vertices of type ['A', 3]
1866
+ Quiver on 3 vertices of type ['A', 3]
1867
+
1868
+ sage: Ts = Q.mutation_class(return_paths=True,up_to_equivalence=False)
1869
+ sage: len(Ts)
1870
+ 14
1871
+ sage: Ts[0]
1872
+ (Quiver on 3 vertices of type ['A', 3], [])
1873
+
1874
+ sage: Ts = Q.mutation_class(show_depth=True)
1875
+ Depth: 0 found: 1 Time: ... s
1876
+ Depth: 1 found: 3 Time: ... s
1877
+ Depth: 2 found: 4 Time: ... s
1878
+
1879
+ sage: Ts = Q.mutation_class(show_depth=True, up_to_equivalence=False)
1880
+ Depth: 0 found: 1 Time: ... s
1881
+ Depth: 1 found: 4 Time: ... s
1882
+ Depth: 2 found: 6 Time: ... s
1883
+ Depth: 3 found: 10 Time: ... s
1884
+ Depth: 4 found: 14 Time: ... s
1885
+
1886
+ TESTS::
1887
+
1888
+ sage: all(len(ClusterQuiver(['A',n]).mutation_class())
1889
+ ....: == ClusterQuiver(['A',n]).mutation_type().class_size()
1890
+ ....: for n in [2..6])
1891
+ True
1892
+
1893
+ sage: all(len(ClusterQuiver(['B',n]).mutation_class())
1894
+ ....: == ClusterQuiver(['B',n]).mutation_type().class_size()
1895
+ ....: for n in [2..6])
1896
+ True
1897
+ """
1898
+ if depth is infinity and not self.is_mutation_finite():
1899
+ raise ValueError('the mutation class can - for infinite mutation'
1900
+ ' types - only be computed up to a given depth')
1901
+ return list(self.mutation_class_iter(depth=depth, show_depth=show_depth,
1902
+ return_paths=return_paths,
1903
+ data_type=data_type,
1904
+ up_to_equivalence=up_to_equivalence,
1905
+ sink_source=sink_source))
1906
+
1907
+ def is_finite(self) -> bool:
1908
+ """
1909
+ Return ``True`` if ``self`` is of finite type.
1910
+
1911
+ EXAMPLES::
1912
+
1913
+ sage: Q = ClusterQuiver(['A',3])
1914
+ sage: Q.is_finite()
1915
+ True
1916
+ sage: Q = ClusterQuiver(['A',[2,2],1])
1917
+ sage: Q.is_finite()
1918
+ False
1919
+ sage: Q = ClusterQuiver([['A',3],['B',3]])
1920
+ sage: Q.is_finite()
1921
+ True
1922
+ sage: Q = ClusterQuiver(['T',[4,4,4]])
1923
+ sage: Q.is_finite()
1924
+ False
1925
+ sage: Q = ClusterQuiver([['A',3],['T',[4,4,4]]])
1926
+ sage: Q.is_finite()
1927
+ False
1928
+ sage: Q = ClusterQuiver([['A',3],['T',[2,2,3]]])
1929
+ sage: Q.is_finite()
1930
+ True
1931
+ sage: Q = ClusterQuiver([['A',3],['D',5]])
1932
+ sage: Q.is_finite()
1933
+ True
1934
+ sage: Q = ClusterQuiver([['A',3],['D',5,1]])
1935
+ sage: Q.is_finite()
1936
+ False
1937
+
1938
+ sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2]])
1939
+ sage: Q.is_finite()
1940
+ False
1941
+
1942
+ sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2],[3,4,1],[4,5,1]])
1943
+ sage: Q.is_finite()
1944
+ False
1945
+ """
1946
+ mt = self.mutation_type()
1947
+ return (type(mt) in [QuiverMutationType_Irreducible,
1948
+ QuiverMutationType_Reducible] and mt.is_finite())
1949
+
1950
+ def is_mutation_finite(self, nr_of_checks=None, return_path=False) -> bool:
1951
+ """
1952
+ Return whether ``self`` is mutation-finite.
1953
+
1954
+ This uses a non-deterministic method by random mutations in various
1955
+ directions. This can result in a wrong answer.
1956
+
1957
+ INPUT:
1958
+
1959
+ - ``nr_of_checks`` -- (default: ``None``) number of mutations applied;
1960
+ Standard is 500*(number of vertices of ``self``)
1961
+ - ``return_path`` -- (default: ``False``) if ``True``, in case of
1962
+ ``self`` not being mutation finite, a path from ``self`` to a quiver
1963
+ with an edge label `(a,-b)` and `a*b > 4` is returned
1964
+
1965
+ ALGORITHM:
1966
+
1967
+ A quiver is mutation infinite if and only if every
1968
+ edge label (a,-b) satisfy a*b > 4.
1969
+ Thus, we apply random mutations in random directions
1970
+
1971
+ EXAMPLES::
1972
+
1973
+ sage: Q = ClusterQuiver(['A',10])
1974
+ sage: Q._mutation_type = None
1975
+ sage: Q.is_mutation_finite()
1976
+ True
1977
+
1978
+ sage: Q = ClusterQuiver([(0,1),(1,2),(2,3),(3,4),
1979
+ ....: (4,5),(5,6),(6,7),(7,8),(2,9)])
1980
+ sage: Q.is_mutation_finite()
1981
+ False
1982
+ """
1983
+ if self._n <= 2:
1984
+ is_finite = True
1985
+ path = None
1986
+ elif not return_path and self._mutation_type == 'undetermined infinite mutation type':
1987
+ is_finite = False
1988
+ elif (isinstance(self._mutation_type, (QuiverMutationType_Irreducible,
1989
+ QuiverMutationType_Reducible))
1990
+ and self._mutation_type.is_mutation_finite()):
1991
+ is_finite = True
1992
+ path = None
1993
+ elif (not return_path and isinstance(self._mutation_type,
1994
+ (QuiverMutationType_Irreducible,
1995
+ QuiverMutationType_Reducible))
1996
+ and not self._mutation_type.is_mutation_finite()):
1997
+ is_finite = False
1998
+ else:
1999
+ # turning dg_component into a canonical form
2000
+ dig6 = _digraph_to_dig6(self.digraph())
2001
+ # and getting the corresponding matrix
2002
+ M = _dig6_to_matrix(dig6)
2003
+
2004
+ is_finite, path = is_mutation_finite(M, nr_of_checks=nr_of_checks)
2005
+ return (is_finite, path) if return_path else is_finite
2006
+
2007
+ def number_of_edges(self):
2008
+ r"""
2009
+ Return the total number of edges on the quiver.
2010
+
2011
+ .. NOTE::
2012
+
2013
+ This only works with non-valued quivers. If used on a
2014
+ non-valued quiver then the positive value is taken to be
2015
+ the number of edges added.
2016
+
2017
+ OUTPUT: integer of the number of edges
2018
+
2019
+ EXAMPLES::
2020
+
2021
+ sage: S = ClusterQuiver(['A',4]); S.number_of_edges()
2022
+ 3
2023
+
2024
+ sage: S = ClusterQuiver(['B',4]); S.number_of_edges()
2025
+ 3
2026
+ """
2027
+ return sum(label[0] for label in self.digraph().edge_labels())
2028
+
2029
+ def relabel(self, relabelling, inplace=True):
2030
+ r"""
2031
+ Return the quiver after doing a relabelling.
2032
+
2033
+ Will relabel the vertices of the quiver.
2034
+
2035
+ INPUT:
2036
+
2037
+ - ``relabelling`` -- dictionary of labels to move around
2038
+ - ``inplace`` -- boolean (default: ``True``); if ``True``, will return
2039
+ a duplicate of the quiver
2040
+
2041
+ EXAMPLES::
2042
+
2043
+ sage: S = ClusterQuiver(['A',4]).relabel({1:'5',2:'go'})
2044
+ """
2045
+ if inplace:
2046
+ quiver = self
2047
+ else:
2048
+ quiver = ClusterQuiver(self)
2049
+
2050
+ # Instantiation variables
2051
+ old_vertices = list(quiver.digraph())
2052
+ digraph_labels = {}
2053
+ dict_labels = {}
2054
+
2055
+ # Organize labels noting that for:
2056
+ # _digraph: {old_vertex: new_vertex}
2057
+ # _vertex_dictionary: {num: new_vertex}
2058
+ if isinstance(relabelling, list):
2059
+ digraph_labels = dict(zip(old_vertices, relabelling))
2060
+ dict_labels = dict(enumerate(relabelling))
2061
+ elif isinstance(relabelling, dict):
2062
+ # need to make sure we map correctly
2063
+ for key in relabelling:
2064
+ val = relabelling[key]
2065
+
2066
+ if key in old_vertices:
2067
+ # If the key is in the old vertices, use that mapping
2068
+ digraph_labels[key] = val
2069
+ # And place it in the right order for our dictionary
2070
+ loc = [i for i, x in enumerate(old_vertices)
2071
+ if x == key][0]
2072
+ dict_labels[loc] = val
2073
+ elif isinstance(key, int) and len(old_vertices) > key:
2074
+ # If the key is an integer, grab that particular vertex
2075
+ digraph_labels[old_vertices[key]] = val
2076
+ # And copy it over to our dictionary
2077
+ dict_labels[key] = val
2078
+
2079
+ quiver._digraph.relabel(digraph_labels)
2080
+ quiver._vertex_dictionary = dict_labels
2081
+ return quiver
2082
+
2083
+ def poincare_semistable(self, theta, d):
2084
+ r"""
2085
+ Return the Poincaré polynomial of the moduli space of semi-stable
2086
+ representations of dimension vector `d`.
2087
+
2088
+ INPUT:
2089
+
2090
+ - ``theta`` -- stability weight, as list or vector of rationals
2091
+ - ``d`` -- dimension vector, as list or vector of coprime integers
2092
+
2093
+ The semi-stability is taken with respect to the slope function
2094
+
2095
+ .. MATH::
2096
+
2097
+ \mu(d) = \theta(d) / \operatorname{dim}(d)
2098
+
2099
+ where `d` is a dimension vector.
2100
+
2101
+ This uses the matrix-inversion algorithm from [Rei2002]_.
2102
+
2103
+ EXAMPLES::
2104
+
2105
+ sage: Q = ClusterQuiver(['A',2])
2106
+ sage: Q.poincare_semistable([1,0],[1,0])
2107
+ 1
2108
+ sage: Q.poincare_semistable([1,0],[1,1])
2109
+ 1
2110
+
2111
+ sage: K2 = ClusterQuiver(matrix([[0,2],[-2,0]]))
2112
+ sage: theta = (1, 0)
2113
+ sage: K2.poincare_semistable(theta, [1,0])
2114
+ 1
2115
+ sage: K2.poincare_semistable(theta, [1,1])
2116
+ v^2 + 1
2117
+ sage: K2.poincare_semistable(theta, [1,2])
2118
+ 1
2119
+ sage: K2.poincare_semistable(theta, [1,3])
2120
+ 0
2121
+
2122
+ sage: K3 = ClusterQuiver(matrix([[0,3],[-3,0]]))
2123
+ sage: theta = (1, 0)
2124
+ sage: K3.poincare_semistable(theta, (2,3))
2125
+ v^12 + v^10 + 3*v^8 + 3*v^6 + 3*v^4 + v^2 + 1
2126
+ sage: K3.poincare_semistable(theta, (3,4))(1)
2127
+ 68
2128
+
2129
+ TESTS::
2130
+
2131
+ sage: Q = ClusterQuiver(['A',2])
2132
+ sage: Q.poincare_semistable([1,0],[2,2])
2133
+ Traceback (most recent call last):
2134
+ ...
2135
+ ValueError: dimension vector d is not coprime
2136
+
2137
+ sage: Q = ClusterQuiver(['A',3])
2138
+ sage: Q.poincare_semistable([1,1,0],[2,3,4])
2139
+ 0
2140
+
2141
+ REFERENCES:
2142
+
2143
+ .. [Rei2002] Markus Reineke, *The Harder-Narasimhan system in quantum
2144
+ groups and cohomology of quiver moduli*, :arxiv:`math/0204059`
2145
+ """
2146
+ if gcd([x for x in d if x]) != 1:
2147
+ raise ValueError("dimension vector d is not coprime")
2148
+ d = vector(ZZ, d)
2149
+ theta = vector(theta)
2150
+
2151
+ n = self.n()
2152
+ b_mat = self.b_matrix()
2153
+ Eu = matrix(ZZ, n, n,
2154
+ lambda i, j: -b_mat[i, j] if b_mat[i, j] > 0 else 0)
2155
+ Eu = 1 + Eu
2156
+ edges = list(self.digraph().edges(sort=True, labels=False))
2157
+
2158
+ mu_d = theta.dot_product(d) / sum(d)
2159
+
2160
+ Li = [0 * d]
2161
+ it = (vector(e) for e in product(*[range(d_i + 1)
2162
+ for d_i in d]))
2163
+ Li += [e for e in it if e.dot_product(theta) > mu_d * sum(e)]
2164
+ Li.append(d)
2165
+ N = len(Li) - 1
2166
+
2167
+ q = polygen(QQ, 'v') # q stands for v**2 until the last line
2168
+
2169
+ def cardinal_RG(d):
2170
+ cardinal_G = prod(q**d_i - q**k for d_i in d for k in range(d_i))
2171
+ cardinal_R = prod(q**(b_mat[i, j] * d[i] * d[j])
2172
+ for i, j in edges)
2173
+ return cardinal_R / cardinal_G
2174
+
2175
+ Reineke_submat = matrix(q.parent().fraction_field(), N, N)
2176
+
2177
+ for i, e in enumerate(Li[:-1]):
2178
+ for j, f in enumerate(Li[1:]):
2179
+ if e == f:
2180
+ Reineke_submat[i, j] = 1
2181
+ continue
2182
+ f_e = f - e
2183
+ if all(x >= 0 for x in f_e):
2184
+ power = (-f_e) * Eu * e
2185
+ Reineke_submat[i, j] = q**power * cardinal_RG(f_e)
2186
+
2187
+ poly = (-1)**N * ((1 - q) * Reineke_submat.det()).numerator()
2188
+ return poly(q**2) # replacing q by v**2
2189
+
2190
+ def d_vector_fan(self):
2191
+ r"""
2192
+ Return the d-vector fan associated with the quiver.
2193
+
2194
+ It is the fan whose maximal cones are generated by the
2195
+ d-matrices of the clusters.
2196
+
2197
+ This is a complete simplicial fan (and even smooth when the
2198
+ initial quiver is acyclic). It only makes sense for quivers of
2199
+ finite type.
2200
+
2201
+ EXAMPLES::
2202
+
2203
+ sage: # needs sage.geometry.polyhedron sage.libs.singular
2204
+ sage: Fd = ClusterQuiver([[1,2]]).d_vector_fan(); Fd
2205
+ Rational polyhedral fan in 2-d lattice N
2206
+ sage: Fd.ngenerating_cones()
2207
+ 5
2208
+ sage: Fd = ClusterQuiver([[1,2],[2,3]]).d_vector_fan(); Fd
2209
+ Rational polyhedral fan in 3-d lattice N
2210
+ sage: Fd.ngenerating_cones()
2211
+ 14
2212
+ sage: Fd.is_smooth()
2213
+ True
2214
+ sage: Fd = ClusterQuiver([[1,2],[2,3],[3,1]]).d_vector_fan(); Fd
2215
+ Rational polyhedral fan in 3-d lattice N
2216
+ sage: Fd.ngenerating_cones()
2217
+ 14
2218
+ sage: Fd.is_smooth()
2219
+ False
2220
+
2221
+ TESTS::
2222
+
2223
+ sage: ClusterQuiver(['A',[2,2],1]).d_vector_fan()
2224
+ Traceback (most recent call last):
2225
+ ...
2226
+ ValueError: only makes sense for quivers of finite type
2227
+ """
2228
+ from sage.geometry.fan import Fan
2229
+
2230
+ if not self.is_finite():
2231
+ raise ValueError('only makes sense for quivers of finite type')
2232
+
2233
+ from .cluster_seed import ClusterSeed
2234
+ from sage.geometry.cone import Cone
2235
+
2236
+ seed = ClusterSeed(self)
2237
+ return Fan([Cone(s.d_matrix().columns())
2238
+ for s in seed.mutation_class()])
2239
+
2240
+ def g_vector_fan(self):
2241
+ r"""
2242
+ Return the g-vector fan associated with the quiver.
2243
+
2244
+ It is the fan whose maximal cones are generated by the
2245
+ g-matrices of the clusters.
2246
+
2247
+ This is a complete simplicial fan. It is only supported for
2248
+ quivers of finite type.
2249
+
2250
+ EXAMPLES::
2251
+
2252
+ sage: # needs sage.geometry.polyhedron
2253
+ sage: Fg = ClusterQuiver([[1,2]]).g_vector_fan(); Fg
2254
+ Rational polyhedral fan in 2-d lattice N
2255
+ sage: Fg.ngenerating_cones()
2256
+ 5
2257
+
2258
+ sage: # needs sage.geometry.polyhedron
2259
+ sage: Fg = ClusterQuiver([[1,2],[2,3]]).g_vector_fan(); Fg
2260
+ Rational polyhedral fan in 3-d lattice N
2261
+ sage: Fg.ngenerating_cones()
2262
+ 14
2263
+ sage: Fg.is_smooth()
2264
+ True
2265
+
2266
+ sage: # needs sage.geometry.polyhedron
2267
+ sage: Fg = ClusterQuiver([[1,2],[2,3],[3,1]]).g_vector_fan(); Fg
2268
+ Rational polyhedral fan in 3-d lattice N
2269
+ sage: Fg.ngenerating_cones()
2270
+ 14
2271
+ sage: Fg.is_smooth()
2272
+ True
2273
+
2274
+ TESTS::
2275
+
2276
+ sage: # needs sage.geometry.polyhedron
2277
+ sage: ClusterQuiver(['A',[2,2],1]).g_vector_fan()
2278
+ Traceback (most recent call last):
2279
+ ...
2280
+ ValueError: only supported for quivers of finite type
2281
+ """
2282
+ from .cluster_seed import ClusterSeed
2283
+ from sage.geometry.cone import Cone
2284
+ from sage.geometry.fan import Fan
2285
+
2286
+ if not (self.is_finite()):
2287
+ raise ValueError('only supported for quivers of finite type')
2288
+ seed = ClusterSeed(self).principal_extension()
2289
+ return Fan([Cone(s.g_matrix().columns())
2290
+ for s in seed.mutation_class()])